298 lines
7.9 KiB
Dart
298 lines
7.9 KiB
Dart
import 'package:cross_file/cross_file.dart';
|
|
import 'package:hoshan/data/model/ai/bots_model.dart';
|
|
|
|
class MessagesModel {
|
|
int? id;
|
|
String? title;
|
|
String? createdAt;
|
|
Bots? bot;
|
|
List<Messages>? messages;
|
|
|
|
MessagesModel({this.id, this.title, this.createdAt, this.bot, this.messages});
|
|
|
|
MessagesModel.fromJson(Map<String, dynamic> json) {
|
|
id = json['id'];
|
|
title = json['title'];
|
|
createdAt = json['created_at'];
|
|
bot = json['bot'] != null ? Bots.fromJson(json['bot']) : null;
|
|
if (json['messages'] != null) {
|
|
messages = <Messages>[];
|
|
json['messages'].forEach((v) {
|
|
messages!.add(Messages.fromJson(v));
|
|
});
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
final Map<String, dynamic> data = <String, dynamic>{};
|
|
data['id'] = id;
|
|
data['title'] = title;
|
|
data['created_at'] = createdAt;
|
|
if (bot != null) {
|
|
data['bot'] = bot!.toJson();
|
|
}
|
|
if (messages != null) {
|
|
data['messages'] = messages!.map((v) => v.toJson()).toList();
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
|
|
class Messages {
|
|
String? id;
|
|
List<Content>? content;
|
|
String? query;
|
|
String? role;
|
|
String? fileUrl;
|
|
bool? like;
|
|
bool? fromBot;
|
|
bool? error;
|
|
XFile? file;
|
|
bool? retry;
|
|
String? createdAt;
|
|
|
|
Messages(
|
|
{this.id,
|
|
this.content,
|
|
this.query,
|
|
this.role,
|
|
this.like,
|
|
this.file,
|
|
this.createdAt,
|
|
this.error = false,
|
|
this.retry = false}) {
|
|
fromBot = (role == 'ai');
|
|
if (content != null && content!.isNotEmpty) {
|
|
// Sort content list: "image" first, then "text"
|
|
content!.sort((a, b) {
|
|
if (a.type == 'image_url' && b.type != 'image_url') {
|
|
return -1; // a comes before b
|
|
} else if (a.type != 'image_url' && b.type == 'image_url') {
|
|
return 1; // b comes before a
|
|
}
|
|
return 0; // maintain original order if both are the same type
|
|
});
|
|
}
|
|
|
|
// _getFile();
|
|
}
|
|
|
|
// Future<void> _getFile() async {
|
|
// file = await ChatbotRepository.createXFileFromUrl(fileUrl ?? '');
|
|
// }
|
|
|
|
Messages.fromJson(Map<String, dynamic> json) {
|
|
error = false;
|
|
id = json['id'];
|
|
if (json['content'] != null) {
|
|
if (json['content'] is List) {
|
|
content = <Content>[];
|
|
json['content'].forEach((v) {
|
|
content!.add(Content.fromJson(v));
|
|
});
|
|
} else if (json['content'] is String) {
|
|
content = [Content(type: 'text', text: json['content'])];
|
|
}
|
|
}
|
|
role = json['role'];
|
|
like = json['like'];
|
|
createdAt = json['created_at'];
|
|
|
|
fromBot = role == 'ai';
|
|
if (content != null && content!.isNotEmpty) {
|
|
// Sort content list: "image" first, then "text"
|
|
content!.sort((a, b) {
|
|
if (a.type == 'image_url' && b.type != 'image_url') {
|
|
return -1; // a comes before b
|
|
} else if (a.type != 'image_url' && b.type == 'image_url') {
|
|
return 1; // b comes before a
|
|
}
|
|
return 0; // maintain original order if both are the same type
|
|
});
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
final Map<String, dynamic> data = <String, dynamic>{};
|
|
data['id'] = id;
|
|
if (content != null) {
|
|
data['content'] = content!.map((v) => v.toJson()).toList();
|
|
}
|
|
data['role'] = role;
|
|
data['like'] = like;
|
|
data['created_at'] = createdAt;
|
|
return data;
|
|
}
|
|
|
|
Messages copyWith(
|
|
{String? id,
|
|
List<Content>? content,
|
|
String? role,
|
|
String? createdAt,
|
|
bool? like,
|
|
bool? error,
|
|
bool? retry,
|
|
String? query,
|
|
XFile? file}) {
|
|
return Messages(
|
|
id: id ?? this.id,
|
|
content: content ?? this.content,
|
|
like: like ?? this.like,
|
|
error: error ?? this.error,
|
|
role: role ?? this.role,
|
|
file: file ?? this.file,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
query: query ?? this.query,
|
|
retry: retry ?? this.retry);
|
|
}
|
|
}
|
|
|
|
class Content {
|
|
String? type;
|
|
String? text;
|
|
FileUrl? imageUrl;
|
|
FileUrl? audioUrl;
|
|
FileUrl? pdfUrl;
|
|
FileUrl? videoUrl;
|
|
|
|
Content(
|
|
{this.type,
|
|
this.text,
|
|
this.imageUrl,
|
|
this.audioUrl,
|
|
this.pdfUrl,
|
|
this.videoUrl});
|
|
|
|
Content.fromJson(Map<String, dynamic> json) {
|
|
type = json['type'];
|
|
text = json['text'];
|
|
imageUrl =
|
|
json['image_url'] != null ? FileUrl.fromJson(json['image_url']) : null;
|
|
audioUrl =
|
|
json['audio_url'] != null ? FileUrl.fromJson(json['audio_url']) : null;
|
|
pdfUrl = json['pdf_url'] != null ? FileUrl.fromJson(json['pdf_url']) : null;
|
|
videoUrl =
|
|
json['video_url'] != null ? FileUrl.fromJson(json['video_url']) : null;
|
|
|
|
// اگر سرور audioUrl/imageUrl/pdfUrl/videoUrl را ارسال نکرده باشد،
|
|
// از روی text چک میکنیم که آیا URL است یا نه
|
|
if (text != null && text!.isNotEmpty) {
|
|
final trimmedText = text!.trim();
|
|
if (trimmedText._isURL()) {
|
|
if (audioUrl == null && trimmedText._isAudio()) {
|
|
audioUrl = FileUrl(url: trimmedText);
|
|
type = 'audio';
|
|
} else if (imageUrl == null && trimmedText._isImage()) {
|
|
imageUrl = FileUrl(url: trimmedText);
|
|
type = 'image';
|
|
} else if (pdfUrl == null && trimmedText._isDocument()) {
|
|
pdfUrl = FileUrl(url: trimmedText);
|
|
type = 'doc';
|
|
} else if (videoUrl == null && trimmedText._isVideo()) {
|
|
videoUrl = FileUrl(url: trimmedText);
|
|
type = 'video';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
final Map<String, dynamic> data = <String, dynamic>{};
|
|
data['type'] = type;
|
|
data['text'] = text;
|
|
if (imageUrl != null) {
|
|
data['image_url'] = imageUrl!.toJson();
|
|
}
|
|
if (audioUrl != null) {
|
|
data['audio_url'] = audioUrl!.toJson();
|
|
}
|
|
if (pdfUrl != null) {
|
|
data['pdf_url'] = pdfUrl!.toJson();
|
|
}
|
|
if (videoUrl != null) {
|
|
data['video_url'] = videoUrl!.toJson();
|
|
}
|
|
return data;
|
|
}
|
|
}
|
|
|
|
class FileUrl {
|
|
String? url;
|
|
String? query;
|
|
bool? attachment;
|
|
|
|
FileUrl({this.url});
|
|
FileUrl.fromJson(Map<String, dynamic> json) {
|
|
url = json['url'];
|
|
query = json['query'];
|
|
attachment = json['attachment'];
|
|
}
|
|
Map<String, dynamic> toJson() {
|
|
final Map<String, dynamic> data = <String, dynamic>{};
|
|
data['url'] = url;
|
|
data['query'] = query;
|
|
data['attachment'] = attachment;
|
|
return data;
|
|
}
|
|
}
|
|
|
|
// توابع کمکی برای چک کردن نوع URL
|
|
extension _StringUrlCheck on String {
|
|
bool _isURL() {
|
|
return startsWith('http://') || startsWith('https://');
|
|
}
|
|
|
|
bool _isImage() {
|
|
final lower = toLowerCase();
|
|
return lower.endsWith('.jpg') ||
|
|
lower.endsWith('.jpeg') ||
|
|
lower.endsWith('.png') ||
|
|
lower.endsWith('.gif') ||
|
|
lower.endsWith('.webp') ||
|
|
lower.endsWith('.bmp') ||
|
|
lower.contains('.jpg?') ||
|
|
lower.contains('.jpeg?') ||
|
|
lower.contains('.png?') ||
|
|
lower.contains('.gif?') ||
|
|
lower.contains('.webp?');
|
|
}
|
|
|
|
bool _isAudio() {
|
|
final lower = toLowerCase();
|
|
return lower.endsWith('.mp3') ||
|
|
lower.endsWith('.wav') ||
|
|
lower.endsWith('.ogg') ||
|
|
lower.endsWith('.m4a') ||
|
|
lower.endsWith('.aac') ||
|
|
lower.endsWith('.flac') ||
|
|
lower.contains('.mp3?') ||
|
|
lower.contains('.wav?') ||
|
|
lower.contains('.ogg?') ||
|
|
lower.contains('.m4a?') ||
|
|
lower.contains('.aac?');
|
|
}
|
|
|
|
bool _isDocument() {
|
|
final lower = toLowerCase();
|
|
return lower.endsWith('.pdf') ||
|
|
lower.endsWith('.doc') ||
|
|
lower.endsWith('.docx') ||
|
|
lower.contains('.pdf?') ||
|
|
lower.contains('.doc?') ||
|
|
lower.contains('.docx?');
|
|
}
|
|
|
|
bool _isVideo() {
|
|
final lower = toLowerCase();
|
|
return lower.endsWith('.mp4') ||
|
|
lower.endsWith('.avi') ||
|
|
lower.endsWith('.mov') ||
|
|
lower.endsWith('.mkv') ||
|
|
lower.endsWith('.webm') ||
|
|
lower.contains('.mp4?') ||
|
|
lower.contains('.avi?') ||
|
|
lower.contains('.mov?') ||
|
|
lower.contains('.mkv?');
|
|
}
|
|
}
|