{"id":37462801,"url":"https://github.com/dimchat/sdk-py","last_synced_at":"2026-01-16T07:05:53.125Z","repository":{"id":57418203,"uuid":"218098468","full_name":"dimchat/sdk-py","owner":"dimchat","description":"Decentralized Instant Messaging (Python SDK)","archived":false,"fork":false,"pushed_at":"2025-09-22T21:19:46.000Z","size":595,"stargazers_count":1,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-27T17:43:38.996Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimchat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-10-28T16:46:13.000Z","updated_at":"2025-09-22T21:19:50.000Z","dependencies_parsed_at":"2023-01-30T16:01:41.926Z","dependency_job_id":"20bdbb40-29fb-4156-869a-820c9fabaefd","html_url":"https://github.com/dimchat/sdk-py","commit_stats":{"total_commits":169,"total_committers":1,"mean_commits":169.0,"dds":0.0,"last_synced_commit":"96a4678f3ad366b9ee84b0f0b237bab8cfc024c3"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/dimchat/sdk-py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fsdk-py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fsdk-py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fsdk-py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fsdk-py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimchat","download_url":"https://codeload.github.com/dimchat/sdk-py/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimchat%2Fsdk-py/sbom","scorecard":{"id":343257,"data":{"date":"2025-08-11","repo":{"name":"github.com/dimchat/sdk-py","commit":"9378213a958e45252caa84ff861920527f833fbf"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":10,"reason":"17 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T06:28:30.210Z","repository_id":57418203,"created_at":"2025-08-18T06:28:30.211Z","updated_at":"2025-08-18T06:28:30.211Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-16T07:05:52.996Z","updated_at":"2026-01-16T07:05:53.114Z","avatar_url":"https://github.com/dimchat.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Decentralized Instant Messaging (Python SDK)\n\n[![License](https://img.shields.io/github/license/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/blob/master/LICENSE)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/dimchat/sdk-py/pulls)\n[![Platform](https://img.shields.io/badge/Platform-Python%203-brightgreen.svg)](https://github.com/dimchat/sdk-py/wiki)\n[![Issues](https://img.shields.io/github/issues/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/issues)\n[![Repo Size](https://img.shields.io/github/repo-size/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/archive/refs/heads/master.zip)\n[![Tags](https://img.shields.io/github/tag/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/tags)\n[![Version](https://img.shields.io/pypi/v/dimsdk)](https://pypi.org/project/dimsdk)\n\n[![Watchers](https://img.shields.io/github/watchers/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/watchers)\n[![Forks](https://img.shields.io/github/forks/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/forks)\n[![Stars](https://img.shields.io/github/stars/dimchat/sdk-py)](https://github.com/dimchat/sdk-py/stargazers)\n[![Followers](https://img.shields.io/github/followers/dimchat)](https://github.com/orgs/dimchat/followers)\n\n## Dependencies\n\n* Latest Versions\n\n| Name | Version | Description |\n|------|---------|-------------|\n| [Ming Ke Ming (名可名)](https://github.com/dimchat/mkm-py) | [![Version](https://img.shields.io/pypi/v/mkm)](https://pypi.org/project/mkm) | Decentralized User Identity Authentication |\n| [Dao Ke Dao (道可道)](https://github.com/dimchat/dkd-py) | [![Version](https://img.shields.io/pypi/v/dkd)](https://pypi.org/project/dkd) | Universal Message Module |\n| [DIMP (去中心化通讯协议)](https://github.com/dimchat/core-py) | [![Version](https://img.shields.io/pypi/v/dimp)](https://pypi.org/project/dimp) | Decentralized Instant Messaging Protocol |\n\n## Extensions\n\n### Content\n\nextends [CustomizedContent](https://github.com/dimchat/core-py#extends-content)\n\n### ContentProcessor\n\n```python\nfrom typing import Optional, List, Dict\n\nfrom dimsdk import *\nfrom dimsdk.cpu import *\n\n\nclass AppCustomizedProcessor(CustomizedContentProcessor):\n    \"\"\"\n        Customized Content Processing Unit\n        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n        Handle content for application customized\n    \"\"\"\n\n    def __init__(self, facebook: Facebook, messenger: Messenger):\n        super().__init__(facebook=facebook, messenger=messenger)\n        self.__handlers: Dict[str, CustomizedContentHandler] = {}\n\n    def set_handler(self, app: str, mod: str, handler: CustomizedContentHandler):\n        key = '%s:%s' % (app, mod)\n        self.__handlers[key] = handler\n\n    # protected\n    def get_handler(self, app: str, mod: str) -\u003e Optional[CustomizedContentHandler]:\n        key = '%s:%s' % (app, mod)\n        return self.__handlers.get(key)\n\n    # Override\n    def _filter(self, app: str, mod: str, content: CustomizedContent, msg: ReliableMessage) -\u003e CustomizedContentHandler:\n        \"\"\" Override for your handler \"\"\"\n        handler = self.get_handler(app=app, mod=mod)\n        if handler is not None:\n            return handler\n        # default handler\n        return super()._filter(app=app, mod=mod, content=content, msg=msg)\n```\n\n```python\nfrom typing import Optional, List, Dict\n\nfrom dimsdk import *\nfrom dimsdk.cpu import *\n\nfrom ...common import GroupHistory\n\n\nclass GroupHistoryHandler(BaseCustomizedHandler):\n    \"\"\" Command Transform:\n\n        +===============================+===============================+\n        |      Customized Content       |      Group Query Command      |\n        +-------------------------------+-------------------------------+\n        |   \"type\" : i2s(0xCC)          |   \"type\" : i2s(0x88)          |\n        |   \"sn\"   : 123                |   \"sn\"   : 123                |\n        |   \"time\" : 123.456            |   \"time\" : 123.456            |\n        |   \"app\"  : \"chat.dim.group\"   |                               |\n        |   \"mod\"  : \"history\"          |                               |\n        |   \"act\"  : \"query\"            |                               |\n        |                               |   \"command\"   : \"query\"       |\n        |   \"group\"     : \"{GROUP_ID}\"  |   \"group\"     : \"{GROUP_ID}\"  |\n        |   \"last_time\" : 0             |   \"last_time\" : 0             |\n        +===============================+===============================+\n    \"\"\"\n\n    # Override\n    async def handle_action(self, act: str, sender: ID, content: CustomizedContent,\n                            msg: ReliableMessage) -\u003e List[Content]:\n        if content.group is None:\n            text = 'Group command error.'\n            return self._respond_receipt(text=text, envelope=msg.envelope, content=content)\n        elif GroupHistory.ACT_QUERY == act:\n            assert GroupHistory.APP == content.application\n            assert GroupHistory.MOD == content.module\n            return await self.__transform_query_command(content=content, msg=msg)\n        else:\n            # assert False, 'unknown action: %s, %s, sender: %s' % (act, content, sender)\n            return await super().handle_action(act=act, sender=sender, content=content, msg=msg)\n\n    async def __transform_query_command(self, content: CustomizedContent, msg: ReliableMessage) -\u003e List[Content]:\n        messenger = self.messenger\n        if messenger is None:\n            assert False, 'messenger lost'\n            # return []\n        info = content.copy_dictionary()\n        info['type'] = ContentType.COMMAND\n        info['command'] = GroupCommand.QUERY\n        query = Content.parse(content=info)\n        if isinstance(query, QueryCommand):\n            return await messenger.process_content(content=query, r_msg=msg)\n        # else:\n        #     assert False, 'query command error: %s, %s, sender: %s' % (query, content, sender)\n        text = 'Query command error.'\n        return self._respond_receipt(text=text, envelope=msg.envelope, content=content)\n```\n\n### ContentProcessorCreator\n\n```python\nfrom typing import Optional\n\nfrom dimsdk import *\nfrom dimsdk.cpu import *\n\nfrom .handshake import *\nfrom .customized import *\n\n\nclass ClientContentProcessorCreator(BaseContentProcessorCreator):\n\n    # noinspection PyMethodMayBeStatic\n    def _create_customized_content_processor(self, facebook: Facebook, messenger: Messenger) -\u003e AppCustomizedProcessor:\n        cpu = AppCustomizedProcessor(facebook=facebook, messenger=messenger)\n        # 'chat.dim.group:history'\n        handler = GroupHistoryHandler(facebook=facebook, messenger=messenger)\n        cpu.set_handler(app=GroupHistory.APP, mod=GroupHistory.MOD, handler=handler)\n        return cpu\n\n    # Override\n    def create_content_processor(self, msg_type: str) -\u003e Optional[ContentProcessor]:\n        # application customized\n        if msg_type == ContentType.APPLICATION or msg_type == 'application':\n            return self._create_customized_content_processor(facebook=self.facebook, messenger=self.messenger)\n        if msg_type == ContentType.CUSTOMIZED or msg_type == 'customized':\n            return self._create_customized_content_processor(facebook=self.facebook, messenger=self.messenger)\n        # others\n        return super().create_content_processor(msg_type=msg_type)\n\n    # Override\n    def create_command_processor(self, msg_type: str, cmd: str) -\u003e Optional[ContentProcessor]:\n        # handshake\n        if cmd == HandshakeCommand.HANDSHAKE:\n            return HandshakeCommandProcessor(facebook=self.facebook, messenger=self.messenger)\n        # others\n        return super().create_command_processor(msg_type=msg_type, cmd=cmd)\n```\n\n## Usage\n\nTo let your **CustomizedContentProcessor** start to work,\nyou must override ```BaseContentProcessorCreator``` for message types:\n\n1. ContentType.APPLICATION \n2. ContentType.CUSTOMIZED\n\nand then set your **creator** for ```GeneralContentProcessorFactory``` in the ```MessageProcessor```.\n\n----\n\nCopyright \u0026copy; 2018-2025 Albert Moky\n[![Followers](https://img.shields.io/github/followers/moky)](https://github.com/moky?tab=followers)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimchat%2Fsdk-py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimchat%2Fsdk-py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimchat%2Fsdk-py/lists"}