Initial commit: JustAMessenger v0.1.0
Серверная часть (Go): - WebSocket сервер с бинарным протоколом - XChaCha20-Poly1305 шифрование - zstd сжатие с дедупликацией (64KB чанки) - SQLite хранилище (WAL режим) - Управление гильдиями, каналами, ролями - Федерация между серверами (ed25519) - REST API + WebSocket endpoints Клиентская часть (Flutter): - Material Design 3 тёмная тема (Discord-like) - WebSocket соединение с сервером - Экраны: сплэш, логин, домашний, гильдии, чат - Модели: пользователи, гильдии, каналы, сообщения, роли - Сервисы: соединение, API, криптография, тема - Виджеты: иконки гильдий, сообщения, ввод чата - Web сборка (PWA) Документация: - AGENTS.md — контекст для ИИ ассистентов - docs/protocol.md — спецификация протокола
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
class Channel {
|
||||
final String id;
|
||||
final String guildId;
|
||||
final String categoryId;
|
||||
final String name;
|
||||
final String type;
|
||||
final String? topic;
|
||||
final int position;
|
||||
final DateTime createdAt;
|
||||
|
||||
Channel({
|
||||
required this.id,
|
||||
required this.guildId,
|
||||
this.categoryId = '',
|
||||
required this.name,
|
||||
this.type = 'text',
|
||||
this.topic,
|
||||
this.position = 0,
|
||||
DateTime? createdAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
|
||||
factory Channel.fromJson(Map<String, dynamic> json) {
|
||||
return Channel(
|
||||
id: json['id'] as String,
|
||||
guildId: json['guild_id'] as String,
|
||||
categoryId: json['category_id'] as String? ?? '',
|
||||
name: json['name'] as String,
|
||||
type: json['type'] as String? ?? 'text',
|
||||
topic: json['topic'] as String?,
|
||||
position: json['position'] as int? ?? 0,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String)
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
bool get isText => type == 'text';
|
||||
bool get isVoice => type == 'voice';
|
||||
bool get isStream => type == 'stream';
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import 'channel.dart';
|
||||
import 'role.dart';
|
||||
|
||||
class Guild {
|
||||
final String id;
|
||||
final String name;
|
||||
final String ownerId;
|
||||
final String? icon;
|
||||
final String? description;
|
||||
final DateTime createdAt;
|
||||
List<Channel> channels;
|
||||
List<Role> roles;
|
||||
List<Category> categories;
|
||||
|
||||
Guild({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.ownerId,
|
||||
this.icon,
|
||||
this.description,
|
||||
DateTime? createdAt,
|
||||
List<Channel>? channels,
|
||||
List<Role>? roles,
|
||||
List<Category>? categories,
|
||||
}) : createdAt = createdAt ?? DateTime.now(),
|
||||
channels = channels ?? [],
|
||||
roles = roles ?? [],
|
||||
categories = categories ?? [];
|
||||
|
||||
factory Guild.fromJson(Map<String, dynamic> json) {
|
||||
return Guild(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
ownerId: json['owner_id'] as String,
|
||||
icon: json['icon'] as String?,
|
||||
description: json['description'] as String?,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String)
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Category {
|
||||
final String id;
|
||||
final String name;
|
||||
final int position;
|
||||
|
||||
Category({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.position,
|
||||
});
|
||||
|
||||
factory Category.fromJson(Map<String, dynamic> json) {
|
||||
return Category(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
position: json['position'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
class Message {
|
||||
final String id;
|
||||
final String channelId;
|
||||
final String authorId;
|
||||
String? authorUsername;
|
||||
List<int>? content;
|
||||
bool encrypted;
|
||||
List<int>? nonce;
|
||||
String messageType;
|
||||
String? replyTo;
|
||||
bool pinned;
|
||||
DateTime? editedAt;
|
||||
final DateTime createdAt;
|
||||
bool sending;
|
||||
bool failed;
|
||||
List<String>? attachments;
|
||||
|
||||
Message({
|
||||
required this.id,
|
||||
required this.channelId,
|
||||
required this.authorId,
|
||||
this.authorUsername,
|
||||
this.content,
|
||||
this.encrypted = false,
|
||||
this.nonce,
|
||||
this.messageType = 'text',
|
||||
this.replyTo,
|
||||
this.pinned = false,
|
||||
this.editedAt,
|
||||
DateTime? createdAt,
|
||||
this.sending = false,
|
||||
this.failed = false,
|
||||
this.attachments,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
|
||||
factory Message.fromJson(Map<String, dynamic> json) {
|
||||
return Message(
|
||||
id: json['id'] as String,
|
||||
channelId: json['channel_id'] as String,
|
||||
authorId: json['author_id'] as String,
|
||||
authorUsername: json['username'] as String?,
|
||||
content: (json['content'] as List<dynamic>?)?.cast<int>(),
|
||||
encrypted: json['encrypted'] as bool? ?? false,
|
||||
nonce: (json['nonce'] as List<dynamic>?)?.cast<int>(),
|
||||
messageType: json['message_type'] as String? ?? 'text',
|
||||
replyTo: json['reply_to'] as String?,
|
||||
pinned: json['pinned'] as bool? ?? false,
|
||||
editedAt: json['edited_at'] != null
|
||||
? DateTime.parse(json['edited_at'] as String)
|
||||
: null,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String)
|
||||
: DateTime.now(),
|
||||
attachments: (json['attachments'] as List<dynamic>?)?.cast<String>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
class Role {
|
||||
final String id;
|
||||
final String guildId;
|
||||
final String name;
|
||||
final int color;
|
||||
final int position;
|
||||
final List<String> permissions;
|
||||
final bool isDefault;
|
||||
|
||||
Role({
|
||||
required this.id,
|
||||
required this.guildId,
|
||||
required this.name,
|
||||
this.color = 0,
|
||||
this.position = 0,
|
||||
List<String>? permissions,
|
||||
this.isDefault = false,
|
||||
}) : permissions = permissions ?? [];
|
||||
|
||||
factory Role.fromJson(Map<String, dynamic> json) {
|
||||
return Role(
|
||||
id: json['id'] as String,
|
||||
guildId: json['guild_id'] as String,
|
||||
name: json['name'] as String,
|
||||
color: json['color'] as int? ?? 0,
|
||||
position: json['position'] as int? ?? 0,
|
||||
permissions: (json['permissions'] as List<dynamic>?)
|
||||
?.cast<String>() ??
|
||||
[],
|
||||
isDefault: json['is_default'] as bool? ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
int get colorValue => color == 0 ? 0xFFB5BAC1 : color;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
class User {
|
||||
final String id;
|
||||
final String username;
|
||||
final String? avatar;
|
||||
final String? bio;
|
||||
final String? publicKey;
|
||||
final DateTime createdAt;
|
||||
|
||||
User({
|
||||
required this.id,
|
||||
required this.username,
|
||||
this.avatar,
|
||||
this.bio,
|
||||
this.publicKey,
|
||||
DateTime? createdAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now();
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) {
|
||||
return User(
|
||||
id: json['id'] as String,
|
||||
username: json['username'] as String,
|
||||
avatar: json['avatar'] as String?,
|
||||
bio: json['bio'] as String?,
|
||||
publicKey: json['public_key'] as String?,
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String)
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'username': username,
|
||||
'avatar': avatar,
|
||||
'bio': bio,
|
||||
'public_key': publicKey,
|
||||
'created_at': createdAt.toIso8601String(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user