небольшие изменения

This commit is contained in:
Jganenokk
2026-07-05 19:07:27 +07:00
parent 26f0094705
commit 9f9b0e0734
214 changed files with 27592 additions and 15353 deletions
+21 -22
View File
@@ -1,5 +1,7 @@
import 'dart:convert';
import '../core/utils/parse.dart';
enum AttachmentType {
photo,
video,
@@ -13,6 +15,8 @@ enum AttachmentType {
share,
call,
inlineKeyboard,
forward,
unknown,
}
String? decodeAttachPreview(dynamic raw) {
@@ -128,7 +132,6 @@ class VideoAttachment extends MessageAttachment {
final int? duration;
final int? size;
/// 0 — обычное видео, 1 — видеосообщение-кружок.
final int? videoType;
bool get isNote => videoType == 1;
@@ -307,7 +310,8 @@ class StickerAttachment extends MessageAttachment {
previewData: decodeAttachPreview(map['previewData']),
baseUrl: (map['url'] ?? map['baseUrl'])?.toString(),
stickerId: map['stickerId']?.toString(),
stickerPackId: map['setId']?.toString() ?? map['stickerPackId']?.toString(),
stickerPackId:
map['setId']?.toString() ?? map['stickerPackId']?.toString(),
lottieUrl: map['lottieUrl']?.toString(),
width: map['width'] as int?,
height: map['height'] as int?,
@@ -358,7 +362,9 @@ class ContactAttachment extends MessageAttachment {
lastName: map['lastName']?.toString(),
phoneNumber: map['phoneNumber']?.toString(),
photoUrl: map['photoUrl']?.toString(),
contactId: map['contactId'] is int ? map['contactId'] as int : int.tryParse(map['contactId']?.toString() ?? ''),
contactId: map['contactId'] is int
? map['contactId'] as int
: int.tryParse(map['contactId']?.toString() ?? ''),
name: map['name']?.toString(),
);
}
@@ -448,8 +454,10 @@ class ControlAttachment extends MessageAttachment {
baseUrl: map['baseUrl']?.toString(),
event: map['event']?.toString(),
title: title,
userIds: (map['userIds'] as List?)?.map((e) => e is int ? e : int.tryParse(e?.toString() ?? '') ?? 0).toList(),
userId: map['userId'] is int ? map['userId'] as int : int.tryParse(map['userId']?.toString() ?? ''),
userIds: map['userIds'] is List ? parseIntList(map['userIds']) : null,
userId: map['userId'] is int
? map['userId'] as int
: int.tryParse(map['userId']?.toString() ?? ''),
);
}
@@ -469,10 +477,8 @@ class PollAttachment extends MessageAttachment {
final int pollId;
final String? title;
const PollAttachment({
required this.pollId,
this.title,
}) : super(type: AttachmentType.poll);
const PollAttachment({required this.pollId, this.title})
: super(type: AttachmentType.poll);
factory PollAttachment.fromMap(Map<String, dynamic> map) {
final id = map['pollId'] ?? map['id'];
@@ -522,10 +528,7 @@ class CallAttachment extends MessageAttachment {
hangupType: map['hangupType']?.toString(),
conversationId: map['conversationId']?.toString(),
joinLink: map['joinLink']?.toString(),
contactIds: (map['contactIds'] as List?)
?.map((e) => e is int ? e : int.tryParse(e?.toString() ?? '') ?? 0)
.toList() ??
const [],
contactIds: parseIntList(map['contactIds']),
);
}
@@ -631,10 +634,8 @@ class InlineKeyboardAttachment extends MessageAttachment {
final String? callbackId;
final List<List<InlineKeyboardButton>> rows;
const InlineKeyboardAttachment({
this.callbackId,
required this.rows,
}) : super(type: AttachmentType.inlineKeyboard);
const InlineKeyboardAttachment({this.callbackId, required this.rows})
: super(type: AttachmentType.inlineKeyboard);
bool get isEmpty => rows.every((row) => row.isEmpty);
@@ -667,9 +668,7 @@ class InlineKeyboardAttachment extends MessageAttachment {
'_type': 'INLINE_KEYBOARD',
if (callbackId != null) 'callbackId': callbackId,
'keyboard': {
'buttons': rows
.map((row) => row.map((b) => b.toMap()).toList())
.toList(),
'buttons': rows.map((row) => row.map((b) => b.toMap()).toList()).toList(),
},
};
}
@@ -695,7 +694,7 @@ class ForwardedMessageAttachment extends MessageAttachment {
this.originalChatId,
this.originalAttachments,
this.originalContact,
}) : super(type: AttachmentType.photo);
}) : super(type: AttachmentType.forward);
factory ForwardedMessageAttachment.fromMap(Map<String, dynamic> map) {
final linkRaw = map['link'];
@@ -764,7 +763,7 @@ class ForwardedMessageAttachment extends MessageAttachment {
class UnknownAttachment extends MessageAttachment {
final Map<String, dynamic> rawData;
const UnknownAttachment(this.rawData) : super(type: AttachmentType.photo);
const UnknownAttachment(this.rawData) : super(type: AttachmentType.unknown);
@override
Map<String, dynamic> toMap() => rawData;
+40
View File
@@ -0,0 +1,40 @@
class ChatInfo {
final Map<String, dynamic> raw;
final List<int> participantIds;
final Set<int> adminIds;
final int? owner;
const ChatInfo({
required this.raw,
required this.participantIds,
required this.adminIds,
required this.owner,
});
factory ChatInfo.fromMap(Map<String, dynamic> map) {
return ChatInfo(
raw: map,
participantIds: _idKeys(map['participants']),
adminIds: _idKeys(map['adminParticipants']).toSet(),
owner: map['owner'] as int?,
);
}
bool isAdmin(int id) => adminIds.contains(id);
bool isOwner(int id) => owner != null && id == owner;
int? get participantsCount => raw['participantsCount'] as int?;
int? get blockedParticipantsCount => raw['blockedParticipantsCount'] as int?;
String? get link => raw['link'] as String?;
String? get description => raw['description'] as String?;
static List<int> _idKeys(Object? source) {
if (source is! Map) return const [];
final out = <int>[];
for (final key in source.keys) {
final id = key is int ? key : int.tryParse(key.toString());
if (id != null) out.add(id);
}
return out;
}
}
+71
View File
@@ -0,0 +1,71 @@
class ContactName {
final String? type;
final String? name;
final String? firstName;
final String? lastName;
const ContactName({this.type, this.name, this.firstName, this.lastName});
factory ContactName.fromMap(Map map) => ContactName(
type: map['type']?.toString(),
name: map['name']?.toString(),
firstName: map['firstName']?.toString(),
lastName: map['lastName']?.toString(),
);
String? get label {
final n = name;
if (n != null && n.trim().isNotEmpty) return n.trim();
final combined = [firstName, lastName]
.where((s) => s != null && s.trim().isNotEmpty)
.map((s) => s!.trim())
.join(' ');
return combined.isEmpty ? null : combined;
}
}
class ContactInfo {
final Map<String, dynamic> raw;
final List<ContactName> names;
const ContactInfo({required this.raw, required this.names});
factory ContactInfo.fromMap(Map<String, dynamic> map) {
final rawNames = map['names'];
final names = <ContactName>[];
if (rawNames is List) {
for (final n in rawNames) {
if (n is Map) names.add(ContactName.fromMap(n));
}
}
return ContactInfo(raw: map, names: names);
}
String? get displayName {
String? firstLabel;
for (final n in names) {
final label = n.label;
if (label == null) continue;
firstLabel ??= label;
if (n.type == 'ONEME') return label;
}
return firstLabel;
}
String? get firstName {
for (final n in names) {
final f = n.firstName;
if (f != null && f.trim().isNotEmpty) return f.trim();
}
return null;
}
String? get avatarUrl => raw['baseUrl'] as String?;
List<String> get options {
final o = raw['options'];
return o is List ? o.whereType<String>().toList() : const [];
}
int? get id => raw['id'] as int?;
}
+50 -25
View File
@@ -56,22 +56,50 @@ class Poll {
}
Poll withStateMap(Map<dynamic, dynamic> stateMap) {
return Poll.fromServerMap({
'pollId': pollId,
'title': title,
'settings': settings,
'version': version,
'answers': [
for (final a in answers) {'answerId': a.answerId, 'text': a.text},
],
'state': stateMap,
});
return _buildFromState(
pollId: pollId,
title: title,
settings: settings,
version: version,
answerIdsAndTexts: [for (final a in answers) (a.answerId, a.text)],
stateMap: stateMap,
);
}
factory Poll.fromServerMap(Map<dynamic, dynamic> map) {
final state = map['state'];
final stateMap = state is Map ? state : const {};
final answerIdsAndTexts = <(int, String)>[];
final rawAnswers = map['answers'];
if (rawAnswers is List) {
for (final a in rawAnswers) {
if (a is! Map) continue;
answerIdsAndTexts.add((
a['answerId'] as int? ?? 0,
a['text']?.toString() ?? '',
));
}
}
return _buildFromState(
pollId: map['pollId'] as int? ?? 0,
title: map['title']?.toString() ?? '',
settings: map['settings'] as int? ?? 0,
version: map['version'] as int? ?? 0,
answerIdsAndTexts: answerIdsAndTexts,
stateMap: stateMap,
);
}
static Poll _buildFromState({
required int pollId,
required String title,
required int settings,
required int version,
required List<(int, String)> answerIdsAndTexts,
required Map<dynamic, dynamic> stateMap,
}) {
final resultsById = <int, Map>{};
final result = stateMap['result'];
if (result is List) {
@@ -83,33 +111,30 @@ class Poll {
}
final answers = <PollAnswer>[];
final rawAnswers = map['answers'];
if (rawAnswers is List) {
for (final a in rawAnswers) {
if (a is! Map) continue;
final id = a['answerId'] as int? ?? 0;
final res = resultsById[id];
answers.add(PollAnswer(
for (final (id, text) in answerIdsAndTexts) {
final res = resultsById[id];
answers.add(
PollAnswer(
answerId: id,
text: a['text']?.toString() ?? '',
text: text,
voteCount: (res?['voteCount'] as num?)?.toInt() ?? 0,
rate: (res?['rate'] as num?)?.toDouble() ?? 0,
votes: _parseVoterIds(res?['votes']),
mine: ((res?['options'] as num?)?.toInt() ?? 0) & 0x1 != 0,
));
}
),
);
}
return Poll(
pollId: map['pollId'] as int? ?? 0,
title: map['title']?.toString() ?? '',
settings: map['settings'] as int? ?? 0,
version: map['version'] as int? ?? 0,
pollId: pollId,
title: title,
settings: settings,
version: version,
total: (stateMap['total'] as num?)?.toInt() ?? 0,
answers: answers,
voterPreviewIds:
(stateMap['voterPreviewIds'] as List?)?.whereType<int>().toList() ??
const [],
const [],
);
}
}
+45
View File
@@ -0,0 +1,45 @@
class ReactionCounter {
final String reaction;
final int count;
const ReactionCounter({required this.reaction, required this.count});
}
class ReactionInfo {
final List<ReactionCounter> counters;
final String? yourReaction;
final int totalCount;
const ReactionInfo({
this.counters = const [],
this.yourReaction,
this.totalCount = 0,
});
static ReactionInfo? fromMap(Map? map) {
if (map == null) return null;
final rawCounters = map['counters'];
if (rawCounters is! List || rawCounters.isEmpty) return null;
final counters = <ReactionCounter>[];
for (final c in rawCounters) {
if (c is! Map) continue;
final reaction = c['reaction']?.toString();
if (reaction == null || reaction.isEmpty) continue;
final rawCount = c['count'];
counters.add(
ReactionCounter(
reaction: reaction,
count: rawCount is int ? rawCount : 0,
),
);
}
if (counters.isEmpty) return null;
final total = map['totalCount'];
return ReactionInfo(
counters: counters,
yourReaction: map['yourReaction']?.toString(),
totalCount: total is int ? total : 0,
);
}
bool get isEmpty => counters.isEmpty;
}