import 'dart:async'; import 'dart:math'; import 'package:desktopapp/classes/stream_element.dart'; import 'package:desktopapp/utils/logger.dart'; import 'package:desktopapp/utils/requests.dart'; import 'package:desktopapp/widgets/popups/mailbox_select.dart'; import 'package:enough_mail/enough_mail.dart'; import 'package:flutter/cupertino.dart'; import 'package:shared_preferences/shared_preferences.dart'; class MailerSettings { static final List defaultMessages = [ 'Un gueux vient de commander de la HOLY', 'Un nouveau gueux vient de commander de la HOLY' ]; static final List defaultWhitelist = ['louis@holyenergy.fr']; const MailerSettings._({required this.messages, required this.whitelist}); static MailerSettings loadSettings(SharedPreferences prefs) { var messages = prefs.getStringList('mailer_messages') ?? defaultMessages; var whitelist = prefs.getStringList('mailer_whitelist') ?? defaultWhitelist; return MailerSettings._(messages: messages, whitelist: whitelist); } final List messages; final List whitelist; Future saveSettings(SharedPreferences prefs) async { await prefs.setStringList('mailer_messages', messages); await prefs.setStringList('mailer_whitelist', whitelist); } } class Mailer { static MailClient? _mailClient; static Timer? _timer; /// High level mail API example static Future connect({ required BuildContext context, required String email, required String password, required StreamController streamController, }) async { Logger.log(LoggerType.info, Mailer, 'Discovering settings for $email'); final config = await Discover.discover(email); if (config == null) { Logger.log(LoggerType.info, Mailer, 'Unable to autodiscover settings for $email'); return false; } Logger.log(LoggerType.info, Mailer, 'Settings for $email : $config'); final account = MailAccount.fromDiscoveredSettings( 'my account', email, password, config); _mailClient = MailClient(account, isLogEnabled: true); try { await _mailClient!.connect(); Logger.log(LoggerType.info, Mailer, 'Connected to $email'); streamController .add(StreamElement(StreamElementType.log, 'Connected to $email')); var mailboxes = await _mailClient!.listMailboxes(); var mailbox = await MailBoxSelect.show(context, mailboxes: mailboxes); await _mailClient!.selectMailbox(mailbox!); streamController.add(StreamElement( StreamElementType.log, '${mailbox.name} selected for $email')); var mailerSettings = MailerSettings.loadSettings(await SharedPreferences.getInstance()); List whitelist = [email, ...mailerSettings.whitelist]; streamController.add(StreamElement(StreamElementType.log, "Listen for emails: ${whitelist.join(', ')}\nMessages : ${mailerSettings.messages.join(', ')}")); _mailClient!.eventBus.on().listen((ImapEvent event) async { await _onMessage( event, streamController, email, mailerSettings, whitelist); }); await _mailClient!.startPolling(const Duration(seconds: 1)); _startDebugProfiler(streamController); return true; } catch (e) { streamController .add(StreamElement(StreamElementType.error, e.toString())); return false; } } static Future disconnect({ required StreamController streamController, }) async { if (_mailClient == null) return; Logger.log(LoggerType.info, Mailer, 'Disconnecting from mail'); _stopDebugProfiler(); await _mailClient!.stopPolling(); await _mailClient!.disconnect(); Logger.log(LoggerType.info, Mailer, 'Disconnected from mail'); _mailClient = null; streamController .add(StreamElement(StreamElementType.log, 'Disconnected from mail')); } static Future _onMessage( ImapEvent event, StreamController streamController, String email, MailerSettings mailerSettings, List whitelist) async { if (event.eventType == ImapEventType.exists) { final evt = event as ImapMessagesExistEvent; if (evt.newMessagesExists <= evt.oldMessagesExists) return; final sequence = MessageSequence(); if (evt.newMessagesExists - evt.oldMessagesExists > 1) { sequence.addRange(evt.oldMessagesExists, evt.newMessagesExists); } else { sequence.add(evt.newMessagesExists); } final messages = await _mailClient!.fetchMessageSequence(sequence, fetchPreference: FetchPreference.envelope); for (final message in messages) { MailAddress? mail = message.from?.cast().firstWhere( (mEmail) => whitelist.contains(mEmail!.email), orElse: () => null); if (mail == null) continue; var mailEvt = MailLoadEvent(message, _mailClient!); _onMailEvent(mailEvt, streamController, mailerSettings); } } } static Future _onMailEvent(MailLoadEvent mailLoadEvent, StreamController streamController, MailerSettings mailerSettings) async { final message = mailerSettings .messages[Random.secure().nextInt(mailerSettings.messages.length)]; await Requests.sendAlert(message, type: AlertType.host); streamController.add(StreamElement(StreamElementType.alert, message)); } static void _startDebugProfiler(StreamController streamController) { _timer = Timer.periodic(const Duration(seconds: 1), (timer) async { if (!_mailClient!.isPolling()) { streamController.add(StreamElement(StreamElementType.warning, 'Mail client is not polling | Restart it')); await _mailClient!.startPolling(); } }); } static void _stopDebugProfiler() { _timer?.cancel(); _timer = null; } }