Decentralized Instant Messaging (Dart SDK)

License PRs Welcome Platform Issues Repo Size Tags Version

Watchers Forks Stars Followers

Dependencies

  • Latest Versions
Name Version Description
Ming Ke Ming (名可名) Version Decentralized User Identity Authentication
Dao Ke Dao (道可道) Version Universal Message Module
DIMP (去中心化通讯协议) Version Decentralized Instant Messaging Protocol

Extensions

Content

extends CustomizedContent

ContentProcessor

///  Customized Content Processing Unit
///  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
///  Handle content for application customized
class CustomizedContentProcessor extends BaseContentProcessor {
  CustomizedContentProcessor(super.facebook, super.messenger);

  @override
  Future<List<Content>> processContent(Content content, ReliableMessage rMsg) async {
    assert(content is CustomizedContent, 'customized content error: $content');
    CustomizedContent customized = content as CustomizedContent;
    var filter = sharedMessageExtensions.customizedFilter;
    // get handler for 'app' & 'mod'
    CustomizedContentHandler handler = filter.filterContent(content, rMsg);
    // handle the action
    return await handler.handleAction(customized, rMsg, messenger!);
  }

}
  • CustomizedContentHandler
///  Handler for Customized Content
///  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
abstract interface class CustomizedContentHandler {

  ///  Do your job
  ///
  /// @param content   - customized content
  /// @param rMsg      - network message
  /// @param messenger - message transceiver
  /// @return responses
  Future<List<Content>> handleAction(CustomizedContent content, ReliableMessage rMsg, Messenger messenger);

}

/// Default Handler
/// ~~~~~~~~~~~~~~~
class BaseCustomizedContentHandler implements CustomizedContentHandler {

  @override
  Future<List<Content>> handleAction(CustomizedContent content, ReliableMessage rMsg, Messenger messenger) async {
    // String app = content.application;
    String app = content.getString('app') ?? '';
    String mod = content.module;
    String act = content.action;
    String text = 'Content not support.';
    return respondReceipt(text, content: content, envelope: rMsg.envelope, extra: {
      'template': 'Customized content (app: \${app}, mod: \${mod}, act: \${act}) not support yet!',
      'replacements': {
        'app': app,
        'mod': mod,
        'act': act,
      }
    });
  }

  //
  //  Convenient responding
  //

  // protected
  List<ReceiptCommand> respondReceipt(String text, {
    required Envelope envelope, Content? content, Map<String, Object>? extra
  }) => [
    // create base receipt command with text & original envelope
    BaseContentProcessor.createReceipt(text, envelope: envelope, content: content, extra: extra)
  ];

}
  • CustomizedContentFilter
/// Factory for CustomizedContentHandler
abstract interface class CustomizedContentFilter {

  ///  Get CustomizedContentHandler for the CustomizedContent
  ///
  /// @param content - customized content
  /// @param rMsg    - network message
  /// @return CustomizedContentHandler
  CustomizedContentHandler filterContent(CustomizedContent content, ReliableMessage rMsg);

}


/// CustomizedContent Extensions
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CustomizedContentFilter _filter = AppCustomizedFilter();

extension CustomizedContentExtension on MessageExtensions {

  CustomizedContentFilter get customizedFilter => _filter;
  set customizedFilter(CustomizedContentFilter filter) => _filter = filter;

}


/// General CustomizedContent Filter
/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class AppCustomizedFilter implements CustomizedContentFilter {

  CustomizedContentHandler defaultHandler = BaseCustomizedContentHandler();

  final Map<String, CustomizedContentHandler> _handlers = {};

  void setContentHandler({
    required String app, required String mod,
    required CustomizedContentHandler handler
  }) => _handlers['$app:$mod'] = handler;

  // protected
  CustomizedContentHandler? getContentHandler({
    required String app, required String mod,
  }) => _handlers['$app:$mod'];

  @override
  CustomizedContentHandler filterContent(CustomizedContent content, ReliableMessage rMsg) {
    // String app = content.application;
    String app = content.getString('app') ?? '';
    String mod = content.module;
    var handler = getContentHandler(app: app, mod: mod);
    if (handler != null) {
      return handler;
    }
    // if the application has too many modules, I suggest you to
    // use different handler to do the jobs for each module.
    throw defaultHandler;
  }

}
  • Example for group querying
/*  Command Transform:

    +===============================+===============================+
    |      Customized Content       |      Group Query Command      |
    +-------------------------------+-------------------------------+
    |   "type" : i2s(0xCC)          |   "type" : i2s(0x88)          |
    |   "sn"   : 123                |   "sn"   : 123                |
    |   "time" : 123.456            |   "time" : 123.456            |
    |   "app"  : "chat.dim.group"   |                               |
    |   "mod"  : "history"          |                               |
    |   "act"  : "query"            |                               |
    |                               |   "command"   : "query"       |
    |   "group"     : "{GROUP_ID}"  |   "group"     : "{GROUP_ID}"  |
    |   "last_time" : 0             |   "last_time" : 0             |
    +===============================+===============================+
 */
class GroupHistoryHandler extends BaseCustomizedContentHandler {

  @override
  Future<List<Content>> handleAction(CustomizedContent content, ReliableMessage rMsg, Messenger messenger) async {
    if (content.group == null) {
      assert(false, 'group command error: $content, sender: ${rMsg.sender}');
      String text = 'Group command error.';
      return respondReceipt(text, envelope: rMsg.envelope, content: content);
    }
    String act = content.action;
    if (act == GroupHistory.ACT_QUERY) {
      // assert(GroupHistory.APP == content.application);
      assert(GroupHistory.MOD == content.module);
      return await transformQueryCommand(content, rMsg, messenger);
    }
    assert(false, 'unknown action: $act, $content, sender: ${rMsg.sender}');
    return await super.handleAction(content, rMsg, messenger);
  }

  // private
  Future<List<Content>> transformQueryCommand(CustomizedContent content, ReliableMessage rMsg, Messenger messenger) async {
    Map info = content.copyMap(false);
    info['type'] = ContentType.COMMAND;
    info['command'] = QueryCommand.QUERY;
    Content? query = Content.parse(info);
    if (query is QueryCommand) {
      return await messenger.processContent(query, rMsg);
    }
    assert(false, 'query command error: $query, $content, sender: ${rMsg.sender}');
    String text = 'Query command error.';
    return respondReceipt(text, envelope: rMsg.envelope, content: content);
  }

}


//  void registerCustomizedHandlers() {
//    var filter = AppCustomizedFilter();
//    // 'chat.dim.group:history'
//    filter.setContentHandler(
//      app: GroupHistory.APP,
//      mod: GroupHistory.MOD,
//      handler: GroupHistoryHandler(),
//    );
//    sharedMessageExtensions.customizedFilter = filter;
//  }

ContentProcessorCreator

import 'package:dimsdk/dimsdk.dart';

import 'customized.dart';
import 'handshake.dart';


class ClientContentProcessorCreator extends BaseContentProcessorCreator {
  ClientContentProcessorCreator(super.facebook, super.messenger);

  @override
  ContentProcessor? createContentProcessor(String msgType) {
    switch (msgType) {

      // application customized
      case ContentType.APPLICATION:
      case ContentType.CUSTOMIZED:
        return CustomizedContentProcessor(facebook!, messenger!);

      // ...
    }
    // others
    return super.createContentProcessor(msgType);
  }

  @override
  ContentProcessor? createCommandProcessor(String msgType, String cmd) {
    switch (cmd) {
    
      case HandshakeCommand.HANDSHAKE:
        return HandshakeCommandProcessor(facebook!, messenger!);
        
      // ...
    }
    // others
    return super.createCommandProcessor(msgType, cmd);
  }

}

Usage

To let your AppCustomizedProcessor start to work, you must override BaseContentProcessorCreator for message types:

  1. ContentType.APPLICATION
  2. ContentType.CUSTOMIZED

and then set your creator for GeneralContentProcessorFactory in the MessageProcessor.


Copyright © 2023-2026 Albert Moky Followers

Libraries

core
DIM-SDK
cpu
DIM-CPU
crypto
DIM-SDK
dimp
DIMP
dimsdk
DIM-SDK
mkm
Ming-Ke-Ming
msg
Dao-Ke-Dao