├── .editorconfig ├── .eslintrc.cjs ├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md └── workflows │ ├── docker.yml │ └── node.js.yml ├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── Dockerfile ├── LICENSE ├── NOTICE ├── Procfile ├── README.md ├── app.json ├── docker-compose-test.yml ├── docker-compose.yml ├── docs ├── botsociety.md ├── chatops.md ├── dialogflow.md └── images │ ├── friday.png │ └── friday.svg ├── package.json ├── scripts ├── run.sh └── token-gateway.sh ├── src ├── app.module.ts ├── applications │ ├── ai-lib │ │ ├── chitchat-api.spec.ts │ │ ├── chitchat-api.ts │ │ ├── chitchat.ts │ │ ├── clue-vocab.txt │ │ ├── dreamily-api.spec.ts │ │ ├── dreamily-api.ts │ │ ├── dreamily.ts │ │ ├── gpt-api.spec.ts │ │ ├── gpt-api.ts │ │ └── gpt.ts │ └── mod.ts ├── config.ts ├── domains │ ├── README.md │ ├── mod.ts │ ├── statuspage │ │ ├── commands │ │ │ ├── handlers │ │ │ │ ├── classify-mo-mt.handler.ts │ │ │ │ ├── mod.ts │ │ │ │ ├── submit-community-members-count.handler.ts │ │ │ │ ├── submit-mobile-originated-count.handler.ts │ │ │ │ └── submit-mobile-terminated-count.handler.ts │ │ │ ├── impls │ │ │ │ ├── classify-mo-mt.command.ts │ │ │ │ ├── mod.ts │ │ │ │ ├── submit-community-members-count.command.ts │ │ │ │ ├── submit-messages-mobile-originated-count.command.ts │ │ │ │ └── submit-messages-mobile-terminated-count.command.ts │ │ │ └── mod.ts │ │ ├── counting.service.ts │ │ ├── events │ │ │ ├── handlers │ │ │ │ ├── community-developers-counted.handler.ts │ │ │ │ ├── message-mobile-originated.handler.ts │ │ │ │ ├── message-mobile-terminated.handler.ts │ │ │ │ └── mod.ts │ │ │ ├── impls │ │ │ │ ├── community-developers-counted.event.ts │ │ │ │ ├── message-mobile-originated.event.ts │ │ │ │ ├── message-mobile-terminated.event.ts │ │ │ │ └── mod.ts │ │ │ └── mod.ts │ │ ├── mod.ts │ │ ├── queries │ │ │ ├── handlers │ │ │ │ ├── get-gitter-members-count.handler.ts │ │ │ │ ├── get-qq-members-count.handler.ts │ │ │ │ ├── get-wechat-members-count.handler.ts │ │ │ │ ├── get-whatsapp-members-count.handler.ts │ │ │ │ ├── get-workpro-members-count.handler.ts │ │ │ │ └── mod.ts │ │ │ ├── impls │ │ │ │ ├── get-gitter-members-count.query.ts │ │ │ │ ├── get-qq-members-count.query.ts │ │ │ │ ├── get-wechat-members-count.query.ts │ │ │ │ ├── get-whatsapp-members-count.query.ts │ │ │ │ ├── get-workpro-members-count.query.ts │ │ │ │ └── mod.ts │ │ │ └── mod.ts │ │ ├── sagas │ │ │ ├── classify-mo-mt.saga.ts │ │ │ ├── count-time.spec.ts │ │ │ ├── count-time.ts │ │ │ ├── mod.ts │ │ │ ├── statuspage.saga.spec.ts │ │ │ └── statuspage.saga.ts │ │ ├── statuspage.module.ts │ │ ├── statuspage.service.ts │ │ └── statuspage.settings.ts │ └── sync-community-rooms │ │ ├── README.md │ │ ├── commands │ │ ├── handlers │ │ │ ├── forward-message-to-gitter-community.handler.ts │ │ │ ├── forward-message-to-qq-community.handler.ts │ │ │ ├── forward-message-to-wechat-community.handler.ts │ │ │ ├── forward-message-to-whatsapp-community.handler.ts │ │ │ ├── forward-message-to-workpro-community.handler.ts │ │ │ ├── forward-text-message-to-gitter-community.handler.ts │ │ │ ├── forward-text-message-to-qq-community.handler.ts │ │ │ ├── forward-text-message-to-wechat-community.handler.ts │ │ │ ├── forward-text-message-to-whatsapp-community.handler.ts │ │ │ ├── forward-text-message-to-workpro-community.handler.ts │ │ │ └── mod.ts │ │ ├── impls │ │ │ ├── forward-message-to-gitter-community-command.command.ts │ │ │ ├── forward-message-to-qq-community-command.command.ts │ │ │ ├── forward-message-to-wechat-community-command.command.ts │ │ │ ├── forward-message-to-whatsapp-community-command.command.ts │ │ │ ├── forward-message-to-workpro-community-command.command.ts │ │ │ ├── forward-text-message-to-gitter-community-command.command.ts │ │ │ ├── forward-text-message-to-qq-community-command.command.ts │ │ │ ├── forward-text-message-to-wechat-community-command.command.ts │ │ │ ├── forward-text-message-to-whatsapp-community-command.command.ts │ │ │ ├── forward-text-message-to-workpro-community-command.command.ts │ │ │ └── mod.ts │ │ └── mod.ts │ │ ├── events │ │ ├── handlers │ │ │ ├── gitter-community-message-received.handler.ts │ │ │ ├── mod.ts │ │ │ ├── puppet-message-received.handler.ts │ │ │ ├── qq-community-message-received.handler.ts │ │ │ ├── wechat-community-message-received.handler.ts │ │ │ ├── whatsapp-community-message-received.handler.ts │ │ │ └── workpro-community-message-received.handler.ts │ │ ├── impls │ │ │ ├── gitter-community-message-received.event.ts │ │ │ ├── mod.ts │ │ │ ├── qq-community-message-received.event.ts │ │ │ ├── wechat-community-message-received.event.ts │ │ │ ├── whatsapp-community-message-received.event.ts │ │ │ └── workpro-community-message-received.event.ts │ │ └── mod.ts │ │ ├── mod.ts │ │ ├── queries │ │ ├── handlers │ │ │ ├── get-message-sayable.handler.ts │ │ │ ├── get-message-signature.handler.ts │ │ │ ├── is-message-type-text.handler.ts │ │ │ └── mod.ts │ │ ├── impl │ │ │ ├── get-message-sayable.query.ts │ │ │ ├── get-message-signature.query.ts │ │ │ ├── is-message-type-text.query.ts │ │ │ └── mod.ts │ │ └── mod.ts │ │ └── sync-community-rooms.module.ts ├── infrastructures │ ├── env-var.ts │ ├── infrastructure.module.ts │ ├── mod.ts │ └── setup-finis.ts ├── main.ts ├── pkg.spec.ts ├── pkg.ts ├── presentations │ ├── commands │ │ ├── handlers │ │ │ ├── chatops.handler.ts │ │ │ └── mod.ts │ │ ├── impls │ │ │ ├── chatops.command.ts │ │ │ └── mod.ts │ │ └── mod.ts │ ├── interfaces │ │ ├── chatops-dto.interface.ts │ │ └── mod.ts │ ├── mod.ts │ ├── presentation.controller.ts │ └── presentation.module.ts ├── pures │ ├── mod.ts │ ├── string-filter.spec.ts │ └── string-filter.ts ├── typings.d.ts ├── wechaty-events │ ├── commands │ │ ├── handlers │ │ │ ├── mod.ts │ │ │ └── send-message.handler.ts │ │ ├── impls │ │ │ ├── mod.ts │ │ │ └── send-message.command.ts │ │ └── mod.ts │ ├── events │ │ ├── handlers │ │ │ └── mod.ts │ │ ├── impls │ │ │ ├── mod.ts │ │ │ └── puppet-message-received.event.ts │ │ └── mod.ts │ ├── mod.ts │ └── wechaty-events.module.ts ├── wechaty-repository │ ├── builders │ │ ├── builder.ts │ │ ├── gitter │ │ │ ├── gitter-builder.ts │ │ │ └── plugins │ │ │ │ ├── mod.ts │ │ │ │ └── vorpals │ │ │ │ └── mod.ts │ │ ├── mod.ts │ │ ├── oa │ │ │ ├── oa-builder.ts │ │ │ └── plugins │ │ │ │ ├── mod.ts │ │ │ │ ├── qnamaker.ts │ │ │ │ └── vorpals │ │ │ │ ├── contrib.ts │ │ │ │ ├── mod.ts │ │ │ │ └── qnamaker.ts │ │ ├── qq │ │ │ ├── plugins │ │ │ │ ├── mod.ts │ │ │ │ └── vorpals │ │ │ │ │ └── mod.ts │ │ │ └── qq-builder.ts │ │ ├── wechat │ │ │ ├── get-io-client.ts │ │ │ ├── get-memory.ts │ │ │ ├── handlers │ │ │ │ ├── on-error.ts │ │ │ │ ├── on-friendship.ts │ │ │ │ ├── on-login.ts │ │ │ │ ├── on-logout.ts │ │ │ │ ├── on-message.ts │ │ │ │ ├── on-room-invite.ts │ │ │ │ ├── on-room-join.ts │ │ │ │ ├── on-room-leave.ts │ │ │ │ ├── on-room-topic.ts │ │ │ │ └── on-scan.ts │ │ │ ├── plugins │ │ │ │ ├── bot5-assistant.ts │ │ │ │ ├── chatops.ts │ │ │ │ ├── crontab.ts │ │ │ │ ├── ding-dong │ │ │ │ │ └── mod.ts │ │ │ │ ├── freshdesk.ts │ │ │ │ ├── friendship-accepter.ts │ │ │ │ ├── heartbeat.ts │ │ │ │ ├── intercom.ts │ │ │ │ ├── mod.ts │ │ │ │ ├── qnamaker.ts │ │ │ │ ├── room-connectors │ │ │ │ │ ├── abbr-room-topic-by-regex.spec.ts │ │ │ │ │ ├── abbr-room-topic-by-regex.ts │ │ │ │ │ ├── bidirectional-mapper.ts │ │ │ │ │ ├── bot5-club │ │ │ │ │ │ └── mod.ts │ │ │ │ │ ├── mod.ts │ │ │ │ │ ├── sender-display-name.ts │ │ │ │ │ ├── unidirectional-mapper.ts │ │ │ │ │ └── wechaty-developers-home │ │ │ │ │ │ ├── broadcast-station.ts │ │ │ │ │ │ ├── home-hq.ts │ │ │ │ │ │ ├── home-mixer.ts │ │ │ │ │ │ ├── language-filtered │ │ │ │ │ │ ├── homes-to-chinese.ts │ │ │ │ │ │ ├── homes-to-english.ts │ │ │ │ │ │ ├── languages-to-homes.ts │ │ │ │ │ │ └── mod.ts │ │ │ │ │ │ ├── mod.ts │ │ │ │ │ │ └── polyglot-to-homes.ts │ │ │ │ ├── room-invitation-accepter.ts │ │ │ │ ├── room-inviters │ │ │ │ │ ├── aidog.ts │ │ │ │ │ ├── bot5.ts │ │ │ │ │ ├── config.ts │ │ │ │ │ ├── mod.ts │ │ │ │ │ ├── polyglot-wechaty.ts │ │ │ │ │ ├── wechaty-chinese.ts │ │ │ │ │ ├── wechaty-english.ts │ │ │ │ │ └── wechaty.ts │ │ │ │ ├── vorpals │ │ │ │ │ ├── ai.ts │ │ │ │ │ ├── chatops-ddr.ts │ │ │ │ │ ├── chatops-friday.ts │ │ │ │ │ ├── chatops-preangel.ts │ │ │ │ │ ├── contributors.ts │ │ │ │ │ ├── direct-message.ts │ │ │ │ │ ├── mod.ts │ │ │ │ │ └── qnamaker.ts │ │ │ │ └── vote-out.ts │ │ │ ├── set-handlers.ts │ │ │ └── wechat-builder.ts │ │ ├── whatsapp │ │ │ ├── plugins │ │ │ │ ├── mod.ts │ │ │ │ └── vorpals │ │ │ │ │ ├── contrib.ts │ │ │ │ │ └── mod.ts │ │ │ └── whatsapp-builder.ts │ │ └── workpro │ │ │ ├── get-io-client.ts │ │ │ ├── get-memory.ts │ │ │ ├── handlers │ │ │ ├── on-error.ts │ │ │ ├── on-friendship.ts │ │ │ ├── on-login.ts │ │ │ ├── on-logout.ts │ │ │ ├── on-message.ts │ │ │ ├── on-room-invite.ts │ │ │ ├── on-room-join.ts │ │ │ ├── on-room-leave.ts │ │ │ ├── on-room-topic.ts │ │ │ └── on-scan.ts │ │ │ ├── plugins │ │ │ ├── bot5-assistant.ts │ │ │ ├── chatops.ts │ │ │ ├── crontab.ts │ │ │ ├── ding-dong │ │ │ │ └── mod.ts │ │ │ ├── freshdesk.ts │ │ │ ├── friendship-accepter.ts │ │ │ ├── heartbeat.ts │ │ │ ├── intercom.ts │ │ │ ├── mod.ts │ │ │ ├── qnamaker.ts │ │ │ ├── room-connectors │ │ │ │ ├── abbr-room-topic-by-regex.spec.ts │ │ │ │ ├── abbr-room-topic-by-regex.ts │ │ │ │ ├── bidirectional-mapper.ts │ │ │ │ ├── bot5-club │ │ │ │ │ └── mod.ts │ │ │ │ ├── mod.ts │ │ │ │ ├── sender-display-name.ts │ │ │ │ ├── unidirectional-mapper.ts │ │ │ │ └── wechaty-developers-home │ │ │ │ │ ├── broadcast-station.ts │ │ │ │ │ ├── home-hq.ts │ │ │ │ │ ├── home-mixer.ts │ │ │ │ │ ├── language-filtered │ │ │ │ │ ├── homes-to-chinese.ts │ │ │ │ │ ├── homes-to-english.ts │ │ │ │ │ ├── languages-to-homes.ts │ │ │ │ │ └── mod.ts │ │ │ │ │ ├── mod.ts │ │ │ │ │ └── polyglot-to-homes.ts │ │ │ ├── room-invitation-accepter.ts │ │ │ ├── room-inviters │ │ │ │ ├── aidog.ts │ │ │ │ ├── bot5.ts │ │ │ │ ├── config.ts │ │ │ │ ├── mod.ts │ │ │ │ ├── polyglot-wechaty.ts │ │ │ │ ├── wechaty-chinese.ts │ │ │ │ ├── wechaty-english.ts │ │ │ │ └── wechaty.ts │ │ │ ├── vorpals │ │ │ │ ├── ai.ts │ │ │ │ ├── chatops-ddr.ts │ │ │ │ ├── chatops-friday.ts │ │ │ │ ├── chatops-preangel.ts │ │ │ │ ├── contributors.ts │ │ │ │ ├── direct-message.ts │ │ │ │ ├── mod.ts │ │ │ │ └── qnamaker.ts │ │ │ └── vote-out.ts │ │ │ ├── set-handlers.ts │ │ │ └── workpro-builder.ts │ ├── mod.ts │ ├── wechaty-repository.module.ts │ └── wechaty.repository.ts └── wechaty-settings │ ├── mod.ts │ ├── named-interface.ts │ ├── settings │ ├── gitter │ │ └── mod.ts │ ├── mod.ts │ ├── oa │ │ └── mod.ts │ ├── qq │ │ └── mod.ts │ ├── wechat │ │ ├── mod.ts │ │ └── rooms │ │ │ ├── bot5.ts │ │ │ ├── chatops.ts │ │ │ ├── polyglot-user-group.ts │ │ │ ├── puppet-user-group.ts │ │ │ └── wechaty-developers.ts │ ├── whatsapp │ │ └── mod.ts │ └── workpro │ │ ├── mod.ts │ │ └── rooms │ │ ├── bot5.ts │ │ ├── chatops.ts │ │ ├── polyglot-user-group.ts │ │ ├── puppet-user-group.ts │ │ └── wechaty-developers.ts │ └── wechaty-settings.module.ts ├── tests ├── controller-e2e.spec.ts └── fixtures │ └── env.ts ├── tsconfig.build.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | max_line_length = 0 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | const rules = { 2 | // https://github.com/eslint/eslint/issues/15299#issuecomment-968099681 3 | "indent": ["error", 2, { "SwitchCase": 1, "ignoredNodes": ["PropertyDefinition"] }], 4 | } 5 | 6 | module.exports = { 7 | extends: '@chatie', 8 | rules, 9 | } 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/about-codeowners/ 3 | # 4 | 5 | /docs/ @lijiarui 6 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | node-version: 18 | - 16 19 | runs-on: ${{ matrix.os }} 20 | timeout-minutes: 10 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: npm 29 | cache-dependency-path: package.json 30 | - run: npm install 31 | - run: npm run build --if-present 32 | - run: npm test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | .env.bak* 60 | 61 | # next.js build output 62 | .next 63 | package-lock.json 64 | *.memory-card.json 65 | 66 | # IDE 67 | .idea 68 | 69 | t/ 70 | t.* 71 | dist/ 72 | .wechaty/ 73 | data/ 74 | .wwebjs_auth/ 75 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 13 4 | 5 | os: 6 | - linux 7 | 8 | cache: 9 | directories: 10 | - node_modules 11 | 12 | install: 13 | - npm install 14 | 15 | script: 16 | - node --version 17 | - npm --version 18 | - npm test 19 | - ./node_modules/.bin/ts-node tests/fixtures/smoke-testing.ts 20 | 21 | notifications: 22 | email: 23 | on_success: change 24 | on_failure: change 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # FROM wechaty/onbuild:next 2 | FROM wechaty/wechaty:next 3 | 4 | ARG NODE_ENV 5 | ENV NODE_ENV $NODE_ENV 6 | 7 | WORKDIR /bot 8 | 9 | COPY package.json . 10 | RUN jq 'del(.dependencies.wechaty)' package.json | sponge package.json \ 11 | && rm -fr node_modules package-lock.json \ 12 | && npm install \ 13 | && sudo rm -fr /tmp/* ~/.npm 14 | COPY . . 15 | RUN npm run build 16 | 17 | CMD [ "npm", "start" ] 18 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Friday BOT is a chatbot for serving Wechaty Community. 2 | Copyright 2019 - now Huan & Wechaty Contributors. 3 | 4 | This product includes software developed at 5 | The Wechaty Organization (https://github.com/wechaty). 6 | 7 | This software contains code derived from the Stackoverflow, 8 | including various modifications by GitHub. 9 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | # Why does SIGTERM handling not work correctly in NodeJS with NPM? 2 | # https://help.heroku.com/ROG3H81R/why-does-sigterm-handling-not-work-correctly-in-nodejs-with-npm 3 | 4 | web: node dist/src/main.js 5 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friday-bot", 3 | "description": "Friday Bot", 4 | "repository": "https://github.com/wechaty/bot5", 5 | "logo": "https://wechaty.js.org/img/icon.png", 6 | "keywords": [ 7 | "wechaty", 8 | "heroku", 9 | "deploy", 10 | "button" 11 | ], 12 | "website": "https://github.com/wechaty/bot5", 13 | "success_url": "/", 14 | "env": { 15 | "WECHATY_TOKEN": { 16 | "description": "Chatie.io Cloud Token", 17 | "required": false 18 | }, 19 | "WECHATY_LOG": { 20 | "description": "Wechaty Log Level", 21 | "required": false 22 | }, 23 | "WECHATY_PUPPET": { 24 | "description": "Wechaty Puppet Name, Default: `wechaty-puppet-puppeteer`. Learn more at: ", 25 | "required": false 26 | }, 27 | "WECHATY_NAME": { 28 | "description": "Wechaty Name Used to Store Memory", 29 | "generator": "secret" 30 | }, 31 | "AWS_ACCESS_KEY_ID": { 32 | "description": "Amazon S3 accessKeyId for Bot Memory", 33 | "required": false 34 | }, 35 | "AWS_SECRET_ACCESS_KEY": { 36 | "description": "Amazon S3 secretAccessKey for Bot Memory", 37 | "required": false 38 | }, 39 | "AWS_REGION": { 40 | "description": "Amazon S3 Region for Bot Memory", 41 | "required": false 42 | }, 43 | "AWS_S3_BUCKET": { 44 | "description": "Amazon S3 Bucket for Bot Memory", 45 | "required": false 46 | } 47 | }, 48 | "engines": { 49 | "node": "10" 50 | }, 51 | "buildpacks": [ 52 | { 53 | "url": "https://github.com/heroku/heroku-buildpack-google-chrome" 54 | }, 55 | { 56 | "url": "heroku/nodejs" 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | friday: 4 | image: friday:test 5 | command: test 6 | container_name: friday_test 7 | environment: 8 | WECHATY_PLUGIN_INTERCOM_TOKEN : "$WECHATY_PLUGIN_INTERCOM_TOKEN" 9 | WECHATY_PLUGIN_INTERCOM_WEBHOOK_PROXY_URL : "$WECHATY_PLUGIN_INTERCOM_WEBHOOK_PROXY_URL" 10 | WECHATY_PLUGIN_FRESHDESK_PORTAL_URL : "$WECHATY_PLUGIN_FRESHDESK_PORTAL_URL" 11 | WECHATY_PLUGIN_FRESHDESK_API_KEY : "$WECHATY_PLUGIN_FRESHDESK_API_KEY" 12 | WECHATY_PLUGIN_FRESHDESK_WEBHOOK_PROXY_URL : "$WECHATY_PLUGIN_FRESHDESK_WEBHOOK_PROXY_URL" 13 | WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY : "$WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY" 14 | WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID : "$WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID" 15 | WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME : "$WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME" 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | friday: 4 | image: wechaty/friday:latest 5 | labels: 6 | - com.centurylinklabs.watchtower.enable=true 7 | init: true 8 | env_file: .env 9 | expose: 10 | - 80 11 | container_name: friday 12 | network_mode: bridge 13 | volumes: 14 | - .wechaty:/bot/.wechaty 15 | - data:/bot/data 16 | environment: 17 | - WEB_PORT=80 18 | - VIRTUAL_HOST=friday.chatie.io 19 | - LETSENCRYPT_HOST=friday.chatie.io 20 | - HTTPS_METHOD=noredirect 21 | healthcheck: 22 | test: ["CMD", "true"] 23 | interval: 1m30s 24 | timeout: 10s 25 | retries: 3 26 | start_period: 40s 27 | volumes: 28 | .wechaty: 29 | data: 30 | -------------------------------------------------------------------------------- /docs/botsociety.md: -------------------------------------------------------------------------------- 1 | ## BotSociety.io embedded testing 2 | 3 | 4 | botsociety 11 | 12 | -------------------------------------------------------------------------------- /docs/chatops.md: -------------------------------------------------------------------------------- 1 | # ChatOps Vorpal Commands 2 | 3 | ## Calculate member number in all Wechaty Developers' Rooms 4 | 5 | ```ts 6 | eval 7 | const roomList = await this.wechaty.Room.findAll({ topic: /Wechaty Developer/i }); 8 | let total = 0; 9 | let text='Summary:\n'; 10 | for (const room of roomList.sort((a,b)=>a.payload.topic.localeCompare(b.payload.topic))) { 11 | const memberList = await room.memberAll(); 12 | text = text+'\n'+await room.topic()+': '+memberList.length; 13 | total = total+memberList.length; 14 | }; 15 | text = text+'\nTotal: '+total; 16 | this.log(text); 17 | ``` 18 | 19 | ## Kickout a member from room 20 | 21 | ```ts 22 | eval 23 | const TOPIC = /Home 6$/i; 24 | const BOB_REGEX = /^白菜$/; 25 | 26 | const matchName = name => BOB_REGEX.test(name); 27 | const room = await this.wechaty.Room.find({ topic: TOPIC }); 28 | if (!room) { 29 | this.log(`room not found for topic: ${TOPIC}`) 30 | return 31 | } 32 | 33 | const memberList = await room.memberAll(); 34 | const bobList = memberList.filter(m => matchName(m.name())); 35 | if (bobList.length === 0) { 36 | await this.log(`${BOB_REGEX} not found in room ${TOPIC}`) ; 37 | return 38 | } else if (bobList.length > 1) { 39 | this.log(`there is more than one ${BOB_REGEX} in room ${TOPIC}`) 40 | return 41 | } 42 | 43 | const bob = bobList[0] 44 | await room.say(`You have violated the code of conduct of our Wechaty Developers' Room, 45 | we have to move you out of this room.`, bob); 46 | await this.wechaty.sleep(3 * 1000) 47 | await room.del(bob); 48 | await room.say('done'); 49 | await this.log('removed ' + BOB_REGEX + ' from ' + TOPIC ) ; 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/dialogflow.md: -------------------------------------------------------------------------------- 1 | # DialogFlow 2 | 3 | Conversational Prototype for Friday.BOT: 4 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /docs/images/friday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wechaty/friday/8844ae7d33137454632e3c5bc30a055e877d0797/docs/images/friday.png -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | set -eo pipefail 5 | 6 | function update () { 7 | docker pull wechaty/friday 8 | git checkout . 9 | git pull 10 | rm -f package-lock.json 11 | npm i 12 | } 13 | 14 | function runSource () { 15 | npm run build 16 | npm start || true 17 | } 18 | 19 | function runDocker () { 20 | docker-compose up 21 | } 22 | 23 | function main () { 24 | 25 | case "$1" in 26 | source) 27 | RUN_CMD=runSource 28 | ;; 29 | 30 | *) 31 | ;& 32 | docker) 33 | RUN_CMD=runDocker 34 | ;; 35 | esac 36 | 37 | case "$2" in 38 | update) 39 | UPDATE_CMD=update 40 | ;; 41 | *) 42 | UPDATE_CMD=true 43 | ;; 44 | esac 45 | 46 | while true 47 | do 48 | "$UPDATE_CMD" 49 | "$RUN_CMD" 50 | sleep 1 51 | done 52 | } 53 | 54 | main "$@" 55 | -------------------------------------------------------------------------------- /scripts/token-gateway.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | # https://stackoverflow.com/a/60406814/1123955 6 | export $(grep -v ^\# .env | xargs) 7 | 8 | # 9 | # Huan(202110): the latest version that Puppet PadLocal@0.4 is compatible with is wechaty@0.78 10 | # 11 | # PadLocal must adaported with Wechaty Puppet API v1.0 before it can be used with Wechaty 1.0 12 | # 13 | WECHATY_IMAGE=wechaty/wechaty:0.78 14 | docker pull "$WECHATY_IMAGE" 15 | 16 | docker run \ 17 | -ti \ 18 | --name friday_wechaty_puppet_service_token_gateway \ 19 | --rm \ 20 | --privileged \ 21 | --network=host \ 22 | -e WECHATY_LOG \ 23 | -e WECHATY_PUPPET="$TOKEN_GATEWAY_WECHATY_PUPPET" \ 24 | -e WECHATY_PUPPET_PADLOCAL_TOKEN="$TOKEN_GATEWAY_WECHATY_PUPPET_PADLOCAL_TOKEN" \ 25 | -e WECHATY_PUPPET_SERVER_PORT="$TOKEN_GATEWAY_WECHATY_PUPPET_SERVER_PORT" \ 26 | -e WECHATY_TOKEN="$WECHATY_PUPPET_SERVICE_TOKEN" \ 27 | "$WECHATY_IMAGE" 28 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common' 2 | 3 | import { PresentationModule } from './presentations/mod.js' 4 | import { DomainModules } from './domains/mod.js' 5 | 6 | @Module({ 7 | imports: [ 8 | ...DomainModules, 9 | PresentationModule, 10 | ], 11 | }) 12 | export class ApplicationModule { 13 | 14 | constructor () {} 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/applications/ai-lib/chitchat-api.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ts-node 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | chatApi, 7 | } from './chitchat-api.js' 8 | 9 | test.skip('chatApi()', async (t) => { 10 | const GOSSIP = '今天天气不错!' 11 | const reply = await chatApi(GOSSIP) 12 | 13 | t.true(reply.length > 0, 'should get reply text: ' + reply) 14 | }) 15 | -------------------------------------------------------------------------------- /src/applications/ai-lib/chitchat-api.ts: -------------------------------------------------------------------------------- 1 | import Axios from 'axios' 2 | import { log } from 'wechaty' 3 | 4 | async function chatApi (gossip: string): Promise { 5 | log.verbose('WechatyVorpalFriday', 'chatApi(%s)', gossip) 6 | 7 | const gossipTokenList = gossip.split('') 8 | const json = { 9 | instances: [ { 10 | length: gossipTokenList.length + 3, 11 | tokens: [ 12 | '', 13 | ...gossipTokenList, 14 | '', 15 | '', 16 | ], 17 | } ], 18 | } 19 | 20 | const ret = await Axios.post( 23 | 'http://dev.chatie.io:8501/v1/models/chat:predict', 24 | json, 25 | ) 26 | 27 | if (ret.status < 200 || ret.status >= 300) { 28 | throw new Error('chatApi request fail: ' + ret.statusText) 29 | } 30 | 31 | // {'predictions': [{'tokens': ['', '你', '好', '啊', '', '', '你', '好', '呀', '!', '', ''], 'length': 11}]} 32 | const predictions = ret.data.predictions 33 | const tokenList = predictions[0].tokens as string[] 34 | 35 | const s2StartIdx = tokenList.indexOf('') 36 | const s2EndIdx = tokenList.indexOf('') 37 | 38 | const reply = tokenList.slice(s2StartIdx + 1, s2EndIdx).join('') 39 | return reply 40 | } 41 | 42 | export { chatApi } 43 | -------------------------------------------------------------------------------- /src/applications/ai-lib/chitchat.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | } from 'wechaty' 4 | import type { 5 | Vorpal, 6 | CommandContext, 7 | Args, 8 | } from 'wechaty-vorpal' 9 | 10 | import { chatApi } from './chitchat-api.js' 11 | 12 | function Chitchat () { 13 | log.verbose('WechatyVorpalFriday', 'Chitchat()') 14 | 15 | return function ChitchatExtension (vorpal: Vorpal) { 16 | log.verbose('WechatyVorpalFriday', 'ChitchatExtension(vorpal)') 17 | 18 | vorpal 19 | .command('chitchat ', 'Small talk with our chatbot!') 20 | .option('-r --repl [time]', 'REPL mode. In this mode just send what you want to say, no command needed any more.') 21 | .action(chitchatAction) 22 | } 23 | } 24 | 25 | interface ChitchatOptions { 26 | repl?: string 27 | } 28 | 29 | async function chitchatAction ( 30 | this: CommandContext, 31 | args: Args, 32 | ): Promise { 33 | log.verbose('WechatyVorpalFriday', 'chitchatAction("%s")', JSON.stringify(args)) 34 | 35 | const options = args.options as any as ChitchatOptions 36 | void options 37 | 38 | const gossip: string = args['gossip'] as string 39 | const reply = await chatApi(gossip) 40 | this.stdout.next(reply) 41 | 42 | return 0 43 | } 44 | 45 | export { Chitchat } 46 | -------------------------------------------------------------------------------- /src/applications/ai-lib/dreamily-api.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ts-node 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | StoryStyle, 7 | dreamilyApi, 8 | } from './dreamily-api.js' 9 | 10 | test.skip('Dreamily API', async (t) => { 11 | const PAYLOAD = { 12 | content: '不想当将军的司机不是好厨子', 13 | style: StoryStyle.imaginative, 14 | } 15 | 16 | // console.info('payload', PAYLOAD) 17 | 18 | const text = await dreamilyApi(PAYLOAD) 19 | 20 | console.info('text:', text) 21 | await t.skip('tbw') 22 | }) 23 | -------------------------------------------------------------------------------- /src/applications/ai-lib/gpt-api.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ts-node 2 | 3 | import { test } from 'tstest' 4 | 5 | import { 6 | gptApi, 7 | } from './gpt-api.js' 8 | 9 | test.skip('gptApi()', async (t) => { 10 | const PREFIX = '天是sky,地是land,云是' 11 | const LENGTH = 5 12 | const result = await gptApi(PREFIX, LENGTH) 13 | 14 | t.true(result.length > LENGTH, 'should get reply text: ' + result) 15 | }) 16 | -------------------------------------------------------------------------------- /src/applications/ai-lib/gpt-api.ts: -------------------------------------------------------------------------------- 1 | // import Axios from 'axios' 2 | import { log } from 'wechaty' 3 | 4 | /** 5 | * Node.js v14 support: 6 | * https://github.com/huggingface/tokenizers/issues/603 7 | */ 8 | // import { BertWordPieceTokenizer } from 'tokenizers' 9 | 10 | // import path from 'path' 11 | 12 | async function gptApi ( 13 | prefix: string, 14 | length = 20, 15 | ): Promise { 16 | log.verbose('WechatyVorpalFriday', 'gptApi(%s, %s)', prefix, length) 17 | 18 | return 'disabled' 19 | 20 | // const vocabFile = path.join(__dirname, 'clue-vocab.txt') 21 | // const wordPieceTokenizer = await BertWordPieceTokenizer.fromOptions({ 22 | // lowercase: true, 23 | // vocabFile, 24 | // }) 25 | // const wpEncoded = await wordPieceTokenizer.encode(prefix, undefined, { 26 | // addSpecialTokens: false, 27 | // }) 28 | 29 | // const idList = wpEncoded.ids 30 | 31 | // const json = { 32 | // instances: [{ 33 | // inp: idList, 34 | // length: length, 35 | // }], 36 | // } 37 | 38 | // const ret = await Axios.post( 39 | // 'http://dev.chatie.io:8506/v1/models/gpt:predict', 40 | // json, 41 | // ) 42 | 43 | // const prediction = ret.data.predictions[0] as number[] 44 | // // console.info(prediction) 45 | // const text = await wordPieceTokenizer.decode(prediction) 46 | // // console.info(typeof text, text) 47 | 48 | // return text.replace(/ /g, '') 49 | } 50 | 51 | export { gptApi } 52 | -------------------------------------------------------------------------------- /src/applications/ai-lib/gpt.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | } from 'wechaty' 4 | import type { 5 | Vorpal, 6 | CommandContext, 7 | Args, 8 | } from 'wechaty-vorpal' 9 | 10 | import { gptApi } from './gpt-api.js' 11 | 12 | function Gpt () { 13 | log.verbose('WechatyVorpalFriday', 'Gpt()') 14 | 15 | return function GptExtension (vorpal: Vorpal) { 16 | log.verbose('WechatyVorpalFriday', 'GptExtension(vorpal)') 17 | 18 | vorpal 19 | .command('gpt ', 'Make GPT writing for you.') 20 | .option('-l --length ', 'the maximum continue writing length') 21 | .action(gptAction) 22 | } 23 | } 24 | 25 | interface GptOptions { 26 | length?: number 27 | } 28 | 29 | async function gptAction ( 30 | this: CommandContext, 31 | args: Args, 32 | ): Promise { 33 | log.verbose('WechatyVorpalFriday', 'gptAction("%s")', JSON.stringify(args)) 34 | 35 | const prefix: string = args['prefix'] as string 36 | const options = args.options as any as GptOptions 37 | 38 | const normalizedOptions = { 39 | length: 20, 40 | ...options, 41 | } 42 | 43 | if (normalizedOptions.length > 100) { 44 | this.stderr.next('gpt continue writing can not longer than 100 words due to the resource limitation of our server. Donation is welcome! https://opencollective.com/wechaty') 45 | return 0 46 | } 47 | 48 | try { 49 | const text = await gptApi(prefix, normalizedOptions.length) 50 | this.stdout.next(`GPT: ${text}`) 51 | return 0 52 | } catch (e) { 53 | console.error(e) 54 | return 1 55 | } 56 | 57 | } 58 | 59 | export { Gpt } 60 | -------------------------------------------------------------------------------- /src/applications/mod.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // https://github.com/motdotla/dotenv/issues/89#issuecomment-596083057 3 | import 'dotenv/config.js' 4 | 5 | import { pkg } from './pkg.js' 6 | 7 | const VERSION = pkg?.version || '0.0.0' 8 | 9 | export { 10 | VERSION, 11 | } 12 | -------------------------------------------------------------------------------- /src/domains/README.md: -------------------------------------------------------------------------------- 1 | ## Domain Models 2 | 3 | ![Layers in a Domain-Driven Design Microservice](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/media/ddd-oriented-microservice/domain-driven-design-microservice.png) 4 | 5 | ## Resources 6 | 7 | - [Design a DDD-oriented microservice](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/ddd-oriented-microservice) 8 | -------------------------------------------------------------------------------- /src/domains/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StatuspageModule, 3 | } from './statuspage/mod.js' 4 | import { 5 | SyncCommunityRoomsModule, 6 | } from './sync-community-rooms/mod.js' 7 | 8 | const DomainModules = [ 9 | StatuspageModule, 10 | SyncCommunityRoomsModule, 11 | ] 12 | 13 | export { 14 | DomainModules, 15 | } 16 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { ClassifyMoMtHandler } from './classify-mo-mt.handler.js' 2 | import { SubmitCommunityMembersCounterHandler } from './submit-community-members-count.handler.js' 3 | import { SubmitMobileOriginatedCountHandler } from './submit-mobile-originated-count.handler.js' 4 | import { SubmitMobileTerminatedCountHandler } from './submit-mobile-terminated-count.handler.js' 5 | 6 | const CommandHandlers = [ 7 | ClassifyMoMtHandler, 8 | SubmitCommunityMembersCounterHandler, 9 | SubmitMobileOriginatedCountHandler, 10 | SubmitMobileTerminatedCountHandler, 11 | ] 12 | 13 | export { 14 | CommandHandlers, 15 | } 16 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/handlers/submit-community-members-count.handler.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { 3 | CommandHandler, 4 | ICommandHandler, 5 | } from '@nestjs/cqrs' 6 | 7 | import { SubmitCommunityMembersCountCommand } from '../impls/mod.js' 8 | 9 | import { StatuspageService } from '../../statuspage.service.js' 10 | 11 | @CommandHandler(SubmitCommunityMembersCountCommand) 12 | export class SubmitCommunityMembersCounterHandler implements ICommandHandler { 13 | 14 | constructor ( 15 | private readonly log: Brolog, 16 | private readonly statuspageService: StatuspageService, 17 | ) {} 18 | 19 | async execute (command: SubmitCommunityMembersCountCommand) { 20 | this.log.verbose('SubmitCommunityMembersCounterHandler', 'execute(%d)', command.counter) 21 | try { 22 | await this.statuspageService.submitCommunityMemberCount(command.counter) 23 | } catch (e) { 24 | this.log.error('SubmitCommunityMembersCounterHandler', 'execute() submitCommunityMemberCount exception: %s', (e as Error).message) 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/handlers/submit-mobile-originated-count.handler.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { 3 | CommandHandler, 4 | ICommandHandler, 5 | } from '@nestjs/cqrs' 6 | 7 | import { SubmitMessagesMobileOriginatedCountCommand } from '../impls/mod.js' 8 | 9 | import { StatuspageService } from '../../statuspage.service.js' 10 | 11 | @CommandHandler(SubmitMessagesMobileOriginatedCountCommand) 12 | export class SubmitMobileOriginatedCountHandler implements ICommandHandler { 13 | 14 | constructor ( 15 | private readonly log: Brolog, 16 | private readonly statuspageService: StatuspageService, 17 | ) { 18 | } 19 | 20 | async execute (command: SubmitMessagesMobileOriginatedCountCommand) { 21 | this.log.verbose('SubmitSentMessagesCounterHandler', 'execute(%d)', command.counter) 22 | try { 23 | await this.statuspageService.submitMobileOriginatedMessageCount(command.counter) 24 | } catch (e) { 25 | this.log.error('SubmitSentMessagesCounterHandler', 'execute() submitMobileOriginatedMessageCount exception: %s', (e as Error).message) 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/handlers/submit-mobile-terminated-count.handler.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { 3 | CommandHandler, 4 | ICommandHandler, 5 | } from '@nestjs/cqrs' 6 | 7 | import { SubmitMessagesMobileTerminatedCountCommand } from '../mod.js' 8 | 9 | import { StatuspageService } from '../../statuspage.service.js' 10 | 11 | @CommandHandler(SubmitMessagesMobileTerminatedCountCommand) 12 | export class SubmitMobileTerminatedCountHandler implements ICommandHandler { 13 | 14 | constructor ( 15 | private readonly log: Brolog, 16 | private readonly statusPageApiService: StatuspageService, 17 | ) {} 18 | 19 | async execute (command: SubmitMessagesMobileTerminatedCountCommand) { 20 | this.log.verbose('SubmitReceivedMessagesCounterHandler', 'execute(%d)', command.counter) 21 | try { 22 | await this.statusPageApiService.submitMobileTerminatedMessageCount(command.counter) 23 | } catch (e) { 24 | this.log.error('SubmitReceivedMessagesCounterHandler', 'execute() submitMobileTerminatedMessageCount exception: %s', (e as Error).message) 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/impls/classify-mo-mt.command.ts: -------------------------------------------------------------------------------- 1 | export class ClassifyMoMtCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './classify-mo-mt.command.js' 2 | export * from './submit-community-members-count.command.js' 3 | export * from './submit-messages-mobile-terminated-count.command.js' 4 | export * from './submit-messages-mobile-originated-count.command.js' 5 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/impls/submit-community-members-count.command.ts: -------------------------------------------------------------------------------- 1 | export class SubmitCommunityMembersCountCommand { 2 | 3 | constructor ( 4 | public readonly counter: number, 5 | ) {} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/impls/submit-messages-mobile-originated-count.command.ts: -------------------------------------------------------------------------------- 1 | export class SubmitMessagesMobileOriginatedCountCommand { 2 | 3 | constructor ( 4 | public readonly counter: number, 5 | ) {} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/impls/submit-messages-mobile-terminated-count.command.ts: -------------------------------------------------------------------------------- 1 | export class SubmitMessagesMobileTerminatedCountCommand { 2 | 3 | constructor ( 4 | public readonly counter: number, 5 | ) {} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/domains/statuspage/commands/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { CommandHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/statuspage/counting.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { 3 | EventBus, 4 | QueryBus, 5 | } from '@nestjs/cqrs' 6 | import { 7 | Interval, 8 | } from '@nestjs/schedule' 9 | import { Brolog } from 'brolog' 10 | 11 | import { 12 | CommunityDevelopersCountedEvent, 13 | } from './events/mod.js' 14 | import { 15 | GetGitterMembersCountQuery, 16 | GetQqMembersCountQuery, 17 | GetWeChatMembersCountQuery, 18 | GetWorkProMembersCountQuery, 19 | GetWhatsAppMembersCountQuery, 20 | } from './queries/mod.js' 21 | 22 | @Injectable() 23 | export class CountingService { 24 | 25 | constructor ( 26 | private readonly eventBus : EventBus, 27 | private readonly log : Brolog, 28 | private readonly queryBus : QueryBus, 29 | ) {} 30 | 31 | /** 32 | * Task Scheduling 33 | * @link https://docs.nestjs.com/techniques/task-scheduling 34 | */ 35 | // @Cron(CronExpression.EVERY_MINUTE, { 36 | // name: 'countCommunityDevelopers', 37 | // }) 38 | @Interval('countCommunityDevelopers', 60 * 1000) 39 | protected async countCommunityDevelopers (): Promise { 40 | this.log.verbose('CountingService', 'countCommunityDevelopers()') 41 | 42 | const countList: number[] = [ 43 | await this.queryBus.execute(new GetQqMembersCountQuery()), 44 | await this.queryBus.execute(new GetWeChatMembersCountQuery()), 45 | await this.queryBus.execute(new GetWorkProMembersCountQuery()), 46 | await this.queryBus.execute(new GetGitterMembersCountQuery()), 47 | await this.queryBus.execute(new GetWhatsAppMembersCountQuery()), 48 | ] 49 | 50 | const finalCount = countList.reduce((acc, cur) => acc + cur, 0) 51 | 52 | this.eventBus.publish( 53 | new CommunityDevelopersCountedEvent(finalCount), 54 | ) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/handlers/community-developers-counted.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type IEventHandler, 3 | EventsHandler, 4 | } from '@nestjs/cqrs' 5 | 6 | import { 7 | CommunityDevelopersCountedEvent, 8 | } from '../impls/mod.js' 9 | 10 | @EventsHandler(CommunityDevelopersCountedEvent) 11 | export class CommunityDevelopersCountedHandler implements IEventHandler { 12 | 13 | constructor () {} 14 | 15 | async handle (_event: CommunityDevelopersCountedEvent) { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/handlers/message-mobile-originated.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type IEventHandler, 3 | EventsHandler, 4 | } from '@nestjs/cqrs' 5 | 6 | import { 7 | MessageMobileOriginatedEvent, 8 | } from '../impls/mod.js' 9 | 10 | @EventsHandler(MessageMobileOriginatedEvent) 11 | export class MessageMobileOriginatedHandler implements IEventHandler { 12 | 13 | constructor () {} 14 | 15 | async handle (_event: MessageMobileOriginatedEvent) { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/handlers/message-mobile-terminated.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type IEventHandler, 3 | EventsHandler, 4 | } from '@nestjs/cqrs' 5 | 6 | import { 7 | MessageMobileTerminatedEvent, 8 | } from '../impls/mod.js' 9 | 10 | @EventsHandler(MessageMobileTerminatedEvent) 11 | export class MessageMobileTerminatedHandler implements IEventHandler { 12 | 13 | constructor () {} 14 | 15 | async handle (_event: MessageMobileTerminatedEvent) { 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { CommunityDevelopersCountedHandler } from './community-developers-counted.handler.js' 2 | import { MessageMobileOriginatedHandler } from './message-mobile-originated.handler.js' 3 | import { MessageMobileTerminatedHandler } from './message-mobile-terminated.handler.js' 4 | 5 | export const EventHandlers = [ 6 | CommunityDevelopersCountedHandler, 7 | MessageMobileOriginatedHandler, 8 | MessageMobileTerminatedHandler, 9 | ] 10 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/impls/community-developers-counted.event.ts: -------------------------------------------------------------------------------- 1 | export class CommunityDevelopersCountedEvent { 2 | 3 | constructor ( 4 | public readonly counter: number, 5 | ) {} 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/impls/message-mobile-originated.event.ts: -------------------------------------------------------------------------------- 1 | export class MessageMobileOriginatedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/impls/message-mobile-terminated.event.ts: -------------------------------------------------------------------------------- 1 | export class MessageMobileTerminatedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './community-developers-counted.event.js' 2 | export * from './message-mobile-terminated.event.js' 3 | export * from './message-mobile-originated.event.js' 4 | -------------------------------------------------------------------------------- /src/domains/statuspage/events/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { EventHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/statuspage/mod.ts: -------------------------------------------------------------------------------- 1 | export { StatuspageModule } from './statuspage.module.js' 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/handlers/get-qq-members-count.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IQueryHandler, 3 | QueryHandler, 4 | } from '@nestjs/cqrs' 5 | import { Brolog } from 'brolog' 6 | import clc from 'cli-color' 7 | 8 | import type { WechatyInterface } from 'wechaty/impls' 9 | 10 | import { WechatyRepository } from '../../../../wechaty-repository/mod.js' 11 | import { QqSettings } from '../../../../wechaty-settings/mod.js' 12 | 13 | import { GetQqMembersCountQuery } from '../impls/mod.js' 14 | import type { OnModuleInit } from '@nestjs/common' 15 | 16 | @QueryHandler(GetQqMembersCountQuery) 17 | export class GetQqMembersCountHandler implements IQueryHandler, OnModuleInit { 18 | 19 | protected wechaty?: WechatyInterface 20 | 21 | constructor ( 22 | private readonly log: Brolog, 23 | private readonly repository: WechatyRepository, 24 | private readonly qqSettings: QqSettings, 25 | ) {} 26 | 27 | async onModuleInit () { 28 | this.wechaty = await this.repository.findByName(this.qqSettings.name) 29 | } 30 | 31 | async execute (_query: GetQqMembersCountQuery) { 32 | this.log.verbose(clc.greenBright('GetQqMembersCount') + 'Handler', 'execute()') 33 | 34 | const topic = /Wechaty|BOT/i 35 | const idSet = new Set() 36 | 37 | if (this.wechaty?.isLoggedIn) { 38 | const roomList = await this.wechaty.Room.findAll({ topic }) 39 | for (const room of roomList) { 40 | const memberList = await room.memberAll() 41 | memberList.forEach(contact => idSet.add(contact.id)) 42 | } 43 | this.log.verbose('GetQqMembersCountHandler', 'getQqMemberIds() got %d members', idSet.size) 44 | } else { 45 | this.log.error('GetQqMembersCountHandler', 'getQqMemberIds() bot is not logged in yet') 46 | } 47 | 48 | return idSet.size 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { GetGitterMembersCountHandler } from './get-gitter-members-count.handler.js' 2 | import { GetQqMembersCountHandler } from './get-qq-members-count.handler.js' 3 | import { GetWeChatMembersCountHandler } from './get-wechat-members-count.handler.js' 4 | import { GetWorkProMembersCountHandler } from './get-workpro-members-count.handler.js' 5 | import { GetWhatsAppMembersCountHandler } from './get-whatsapp-members-count.handler.js' 6 | 7 | export const QueryHandlers = [ 8 | GetGitterMembersCountHandler, 9 | GetQqMembersCountHandler, 10 | GetWeChatMembersCountHandler, 11 | GetWorkProMembersCountHandler, 12 | GetWhatsAppMembersCountHandler, 13 | ] 14 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/get-gitter-members-count.query.ts: -------------------------------------------------------------------------------- 1 | export class GetGitterMembersCountQuery {} 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/get-qq-members-count.query.ts: -------------------------------------------------------------------------------- 1 | export class GetQqMembersCountQuery {} 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/get-wechat-members-count.query.ts: -------------------------------------------------------------------------------- 1 | export class GetWeChatMembersCountQuery {} 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/get-whatsapp-members-count.query.ts: -------------------------------------------------------------------------------- 1 | export class GetWhatsAppMembersCountQuery {} 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/get-workpro-members-count.query.ts: -------------------------------------------------------------------------------- 1 | export class GetWorkProMembersCountQuery {} 2 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './get-gitter-members-count.query.js' 2 | export * from './get-qq-members-count.query.js' 3 | export * from './get-wechat-members-count.query.js' 4 | export * from './get-workpro-members-count.query.js' 5 | export * from './get-whatsapp-members-count.query.js' 6 | -------------------------------------------------------------------------------- /src/domains/statuspage/queries/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { QueryHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/statuspage/sagas/classify-mo-mt.saga.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { ICommand, ofType, Saga } from '@nestjs/cqrs' 3 | import type { Observable } from 'rxjs' 4 | import { 5 | map, 6 | } from 'rxjs/operators' 7 | import { PuppetMessageReceivedEvent } from '../../../wechaty-events/mod.js' 8 | import { ClassifyMoMtCommand } from '../commands/impls/classify-mo-mt.command.js' 9 | 10 | @Injectable() 11 | export class ClassifyMoMtSaga { 12 | 13 | @Saga() 14 | puppetMessageReceived = (events$: Observable): Observable => { 15 | return events$ 16 | .pipe( 17 | ofType( 18 | PuppetMessageReceivedEvent, 19 | ), 20 | map(event => 21 | new ClassifyMoMtCommand(event.puppetId, event.messageId), 22 | ), 23 | ) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/domains/statuspage/sagas/count-time.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | /** 3 | * Wechaty - https://github.com/chatie/wechaty 4 | * 5 | * @copyright 2022 Huan LI 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | import { 21 | test, 22 | testSchedulerRunner, 23 | } from 'tstest' 24 | 25 | import TimeConstants from 'time-constants' 26 | 27 | import { 28 | countTime, 29 | } from './count-time.js' 30 | 31 | /** 32 | * See: https://github.com/ReactiveX/rxjs/blob/master/doc/writing-marble-tests.md 33 | * See Also: https://github.com/ohjames/rxjs-websockets/blob/master/src/index.spec.ts 34 | */ 35 | test('RxJS operator: countTime()', testSchedulerRunner(m => { 36 | const actual = '-0-0 996ms 59s 4m 0-0-0- 994ms 59s 4m - 999ms 59s 4m - 999ms 59s 4m | ' 37 | const expected = ' 5m 2----- 994ms 59s 4m 3 999ms 59s 4m 0 999ms 59s 4m (0|)' 38 | 39 | const result$ = m.cold(actual).pipe( 40 | countTime(5 * TimeConstants.MINUTE), 41 | ) 42 | 43 | m.expectObservable(result$).toBe(expected) 44 | })) 45 | -------------------------------------------------------------------------------- /src/domains/statuspage/sagas/count-time.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Observable, 3 | } from 'rxjs' 4 | import { 5 | windowTime, 6 | mergeMap, 7 | count, 8 | } from 'rxjs/operators' 9 | import TimeConstants from 'time-constants' 10 | 11 | /** 12 | * SO: Count events over a period of time and yield the sum once every second in RxJS 13 | * @link https://stackoverflow.com/a/38223648/1123955 14 | * 15 | * @param {number} periodMs: period of time in milliseconds, default 5 minutes 16 | */ 17 | const countTime = ( 18 | periodMs = 5 * TimeConstants.MINUTE, 19 | ) => ( 20 | obs: Observable, 21 | ) => obs.pipe( 22 | windowTime(periodMs), 23 | mergeMap(window => window.pipe( 24 | count(), 25 | )), 26 | ) 27 | 28 | export { 29 | countTime, 30 | } 31 | -------------------------------------------------------------------------------- /src/domains/statuspage/sagas/mod.ts: -------------------------------------------------------------------------------- 1 | import { ClassifyMoMtSaga } from './classify-mo-mt.saga.js' 2 | import { StatuspageSaga } from './statuspage.saga.js' 3 | 4 | export const SagaHandlers = [ 5 | ClassifyMoMtSaga, 6 | StatuspageSaga, 7 | ] 8 | -------------------------------------------------------------------------------- /src/domains/statuspage/sagas/statuspage.saga.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { 3 | ICommand, 4 | ofType, 5 | Saga, 6 | } from '@nestjs/cqrs' 7 | import type { 8 | Observable, 9 | } from 'rxjs' 10 | import { 11 | map, 12 | } from 'rxjs/operators' 13 | 14 | import { 15 | SubmitCommunityMembersCountCommand, 16 | SubmitMessagesMobileTerminatedCountCommand, 17 | SubmitMessagesMobileOriginatedCountCommand, 18 | } from '../commands/mod.js' 19 | import { 20 | MessageMobileTerminatedEvent, 21 | MessageMobileOriginatedEvent, 22 | CommunityDevelopersCountedEvent, 23 | } from '../events/mod.js' 24 | 25 | import { countTime } from './count-time.js' 26 | 27 | @Injectable() 28 | export class StatuspageSaga { 29 | 30 | @Saga() 31 | communityDevelopersCounted = (events$: Observable): Observable => { 32 | return events$ 33 | .pipe( 34 | ofType(CommunityDevelopersCountedEvent), 35 | map(event => new SubmitCommunityMembersCountCommand(event.counter)), 36 | ) 37 | } 38 | 39 | @Saga() 40 | messageReceived = (events$: Observable): Observable => events$.pipe( 41 | ofType(MessageMobileTerminatedEvent), 42 | countTime(), 43 | map(count => new SubmitMessagesMobileTerminatedCountCommand(count)), 44 | ) 45 | 46 | @Saga() 47 | messageSent = (events$: Observable): Observable => events$.pipe( 48 | ofType(MessageMobileOriginatedEvent), 49 | countTime(), 50 | map(count => new SubmitMessagesMobileOriginatedCountCommand(count)), 51 | ) 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/domains/statuspage/statuspage.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common' 2 | import { CqrsModule } from '@nestjs/cqrs' 3 | import { ScheduleModule } from '@nestjs/schedule' 4 | 5 | import { InfrastructureModule } from '../../infrastructures/mod.js' 6 | import { WechatyRepositoryModule } from '../../wechaty-repository/mod.js' 7 | import { WechatySettingsModule } from '../../wechaty-settings/mod.js' 8 | 9 | import { CommandHandlers } from './commands/mod.js' 10 | import { EventHandlers } from './events/mod.js' 11 | import { QueryHandlers } from './queries/mod.js' 12 | import { SagaHandlers } from './sagas/mod.js' 13 | 14 | import { CountingService } from './counting.service.js' 15 | import { StatuspageService } from './statuspage.service.js' 16 | import { StatuspageSettings } from './statuspage.settings.js' 17 | 18 | @Module({ 19 | imports: [ 20 | CqrsModule, 21 | InfrastructureModule, 22 | ScheduleModule.forRoot(), 23 | WechatyRepositoryModule, 24 | WechatySettingsModule, 25 | ], 26 | providers: [ 27 | ...CommandHandlers, 28 | ...EventHandlers, 29 | ...QueryHandlers, 30 | ...SagaHandlers, 31 | CountingService, 32 | StatuspageService, 33 | StatuspageSettings, 34 | ], 35 | }) 36 | export class StatuspageModule {} 37 | -------------------------------------------------------------------------------- /src/domains/statuspage/statuspage.settings.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { Injectable } from '@nestjs/common' 3 | 4 | import { EnvVar } from '../../infrastructures/mod.js' 5 | 6 | @Injectable() 7 | class StatuspageSettings { 8 | 9 | public readonly apiKey: string 10 | public readonly pageId: string 11 | public readonly members: string 12 | public readonly receivedMessages: string 13 | public readonly sentMessages: string 14 | 15 | constructor ( 16 | private readonly log: Brolog, 17 | envVar: EnvVar, 18 | ) { 19 | this.log.verbose('StatusPageSettings', 'constructor()') 20 | 21 | this.apiKey = envVar 22 | .get('STATUS_PAGE_API_KEY') 23 | .required(true) 24 | .asString() 25 | 26 | this.pageId = envVar 27 | .get('STATUS_PAGE_PAGE_ID') 28 | .required(true) 29 | .asString() 30 | 31 | this.members = envVar 32 | .get('STATUS_PAGE_METRIC_ID_MEMBERS') 33 | .required(true) 34 | .asString() 35 | 36 | this.receivedMessages = envVar 37 | .get('STATUS_PAGE_METRIC_ID_RECEIVED_MESSAGES') 38 | .required(true) 39 | .asString() 40 | 41 | this.sentMessages = envVar 42 | .get('STATUS_PAGE_METRIC_ID_SENT_MESSAGES') 43 | .required(true) 44 | .asString() 45 | } 46 | 47 | } 48 | 49 | export { 50 | StatuspageSettings, 51 | } 52 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { ForwardMessageToGitterCommunityHandler } from './forward-message-to-gitter-community.handler.js' 2 | import { ForwardTextMessageToGitterCommunityHandler } from './forward-text-message-to-gitter-community.handler.js' 3 | import { ForwardMessageToQqCommunityHandler } from './forward-message-to-qq-community.handler.js' 4 | import { ForwardTextMessageToQqCommunityHandler } from './forward-text-message-to-qq-community.handler.js' 5 | import { ForwardMessageToWeChatCommunityHandler } from './forward-message-to-wechat-community.handler.js' 6 | import { ForwardMessageToWorkProCommunityHandler } from './forward-message-to-workpro-community.handler.js' 7 | import { ForwardTextMessageToWeChatCommunityHandler } from './forward-text-message-to-wechat-community.handler.js' 8 | import { ForwardTextMessageToWorkProCommunityHandler } from './forward-text-message-to-workpro-community.handler.js' 9 | import { ForwardMessageToWhatsAppCommunityHandler } from './forward-message-to-whatsapp-community.handler.js' 10 | import { ForwardTextMessageToWhatsAppCommunityHandler } from './forward-text-message-to-whatsapp-community.handler.js' 11 | 12 | const CommandHandlers = [ 13 | ForwardMessageToGitterCommunityHandler, 14 | ForwardTextMessageToGitterCommunityHandler, 15 | ForwardMessageToQqCommunityHandler, 16 | ForwardTextMessageToQqCommunityHandler, 17 | ForwardMessageToWeChatCommunityHandler, 18 | ForwardMessageToWorkProCommunityHandler, 19 | ForwardTextMessageToWeChatCommunityHandler, 20 | ForwardTextMessageToWorkProCommunityHandler, 21 | ForwardMessageToWhatsAppCommunityHandler, 22 | ForwardTextMessageToWhatsAppCommunityHandler, 23 | ] 24 | 25 | export { 26 | CommandHandlers, 27 | } 28 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-message-to-gitter-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardMessageToGitterCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-message-to-qq-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardMessageToQqCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-message-to-wechat-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardMessageToWeChatCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-message-to-whatsapp-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardMessageToWhatsAppCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-message-to-workpro-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardMessageToWorkProCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-text-message-to-gitter-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardTextMessageToGitterCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-text-message-to-qq-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardTextMessageToQqCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-text-message-to-wechat-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardTextMessageToWeChatCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-text-message-to-whatsapp-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardTextMessageToWhatsAppCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/forward-text-message-to-workpro-community-command.command.ts: -------------------------------------------------------------------------------- 1 | export class ForwardTextMessageToWorkProCommunityCommand { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './forward-message-to-gitter-community-command.command.js' 2 | export * from './forward-text-message-to-gitter-community-command.command.js' 3 | export * from './forward-message-to-qq-community-command.command.js' 4 | export * from './forward-text-message-to-qq-community-command.command.js' 5 | export * from './forward-message-to-wechat-community-command.command.js' 6 | export * from './forward-message-to-workpro-community-command.command.js' 7 | export * from './forward-text-message-to-wechat-community-command.command.js' 8 | export * from './forward-text-message-to-workpro-community-command.command.js' 9 | export * from './forward-message-to-whatsapp-community-command.command.js' 10 | export * from './forward-text-message-to-whatsapp-community-command.command.js' 11 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/commands/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { CommandHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { GitterCommunityMessageReceivedHandler } from './gitter-community-message-received.handler.js' 2 | import { PuppetMessageReceivedHandler } from './puppet-message-received.handler.js' 3 | import { QqCommunityMessageReceivedHandler } from './qq-community-message-received.handler.js' 4 | import { WeChatCommunityMessageReceivedHandler } from './wechat-community-message-received.handler.js' 5 | import { WhatsAppCommunityMessageReceivedHandler } from './whatsapp-community-message-received.handler.js' 6 | 7 | export const EventHandlers = [ 8 | GitterCommunityMessageReceivedHandler, 9 | PuppetMessageReceivedHandler, 10 | QqCommunityMessageReceivedHandler, 11 | WeChatCommunityMessageReceivedHandler, 12 | WhatsAppCommunityMessageReceivedHandler, 13 | ] 14 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/gitter-community-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class GitterCommunityMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './gitter-community-message-received.event.js' 2 | export * from './qq-community-message-received.event.js' 3 | export * from './wechat-community-message-received.event.js' 4 | export * from './workpro-community-message-received.event.js' 5 | export * from './whatsapp-community-message-received.event.js' 6 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/qq-community-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class QqCommunityMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/wechat-community-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class WeChatCommunityMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/whatsapp-community-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class WhatsAppCommunityMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/impls/workpro-community-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class WorkProCommunityMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/events/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { EventHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/mod.ts: -------------------------------------------------------------------------------- 1 | export { SyncCommunityRoomsModule } from './sync-community-rooms.module.js' 2 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/handlers/get-message-sayable.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IQueryHandler, 3 | QueryHandler, 4 | } from '@nestjs/cqrs' 5 | import { Brolog } from 'brolog' 6 | import * as WECHATY from 'wechaty' 7 | import type * as PUPPET from 'wechaty-puppet' 8 | 9 | import { WechatyRepository } from '../../../../wechaty-repository/mod.js' 10 | 11 | import { GetMessageSayableQuery } from '../mod.js' 12 | 13 | @QueryHandler(GetMessageSayableQuery) 14 | export class GetMessageSayableHandler implements IQueryHandler { 15 | 16 | constructor ( 17 | private readonly log: Brolog, 18 | private readonly repository: WechatyRepository, 19 | ) {} 20 | 21 | async execute (query: GetMessageSayableQuery): Promise { 22 | this.log.verbose('GetMessageSayableHandler', 'execute({puppetId: %s, messageId: %s})', 23 | query.puppetId, 24 | query.messageId, 25 | ) 26 | 27 | const wechaty = await this.repository.findByPuppetId(query.puppetId) 28 | if (!wechaty) { 29 | this.log.warn('GetMessageSayableHandler', 'execute() no wechaty found for puppetId: %s', query.puppetId) 30 | return undefined 31 | } 32 | 33 | const message = await wechaty.Message.find({ id: query.messageId }) 34 | if (!message) { 35 | this.log.warn('GetMessageSayableHandler', 'execute() no message found for messageId: %s', query.messageId) 36 | return undefined 37 | } 38 | 39 | const sayable = await WECHATY.helpers.messageToSayable(message) 40 | if (!sayable) { 41 | this.log.warn('GetMessageSayableHandler', 'execute() no sayable found for messageId: %s', query.messageId) 42 | return undefined 43 | } 44 | 45 | const payload = await WECHATY.helpers.sayableToPayload(sayable) 46 | return payload 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/handlers/is-message-type-text.handler.ts: -------------------------------------------------------------------------------- 1 | import { IQueryHandler, QueryHandler } from '@nestjs/cqrs' 2 | import { Brolog } from 'brolog' 3 | import * as PUPPET from 'wechaty-puppet' 4 | 5 | import { WechatyRepository } from '../../../../wechaty-repository/mod.js' 6 | 7 | import { IsMessageTypeTextQuery } from '../mod.js' 8 | 9 | @QueryHandler(IsMessageTypeTextQuery) 10 | export class IsMessageTypeTextHandler implements IQueryHandler { 11 | 12 | constructor ( 13 | private readonly log: Brolog, 14 | private readonly repository: WechatyRepository, 15 | ) {} 16 | 17 | async execute (query: IsMessageTypeTextQuery): Promise { 18 | this.log.verbose('IsMessageTypeTextHandler', 'execute({puppetId: %s, messageId: %s})', 19 | query.puppetId, 20 | query.messageId, 21 | ) 22 | 23 | const wechaty = await this.repository.findByPuppetId(query.puppetId) 24 | if (!wechaty) { 25 | throw new Error('puppetId not found: ' + query.puppetId) 26 | } 27 | 28 | const message = await wechaty.Message.find({ id: query.messageId }) 29 | if (!message) { 30 | throw new Error('messageId not found: ' + query.messageId) 31 | } 32 | 33 | return message.type() === PUPPET.types.Message.Text 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { GetMessageSayableHandler } from './get-message-sayable.handler.js' 2 | import { GetMessageSignatureHandler } from './get-message-signature.handler.js' 3 | import { IsMessageTypeTextHandler } from './is-message-type-text.handler.js' 4 | 5 | export const QueryHandlers = [ 6 | GetMessageSignatureHandler, 7 | GetMessageSayableHandler, 8 | IsMessageTypeTextHandler, 9 | ] 10 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/impl/get-message-sayable.query.ts: -------------------------------------------------------------------------------- 1 | export class GetMessageSayableQuery { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/impl/get-message-signature.query.ts: -------------------------------------------------------------------------------- 1 | export class GetMessageSignatureQuery { 2 | 3 | constructor ( 4 | public readonly type: 'Markdown' | 'Plaintext', 5 | public readonly puppetId: string, 6 | public readonly messageId: string, 7 | ) {} 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/impl/is-message-type-text.query.ts: -------------------------------------------------------------------------------- 1 | export class IsMessageTypeTextQuery { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/impl/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './get-message-signature.query.js' 2 | export * from './get-message-sayable.query.js' 3 | export * from './is-message-type-text.query.js' 4 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/queries/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impl/mod.js' 2 | export { QueryHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/domains/sync-community-rooms/sync-community-rooms.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common' 2 | import { CqrsModule } from '@nestjs/cqrs' 3 | 4 | import { InfrastructureModule } from '../../infrastructures/mod.js' 5 | import { WechatyRepositoryModule } from '../../wechaty-repository/mod.js' 6 | import { WechatySettingsModule } from '../../wechaty-settings/mod.js' 7 | 8 | import { CommandHandlers } from './commands/mod.js' 9 | import { EventHandlers } from './events/mod.js' 10 | import { QueryHandlers } from './queries/mod.js' 11 | 12 | @Module({ 13 | imports: [ 14 | CqrsModule, 15 | InfrastructureModule, 16 | WechatyRepositoryModule, 17 | WechatySettingsModule, 18 | ], 19 | providers: [ 20 | ...CommandHandlers, 21 | ...EventHandlers, 22 | ...QueryHandlers, 23 | ], 24 | }) 25 | export class SyncCommunityRoomsModule {} 26 | -------------------------------------------------------------------------------- /src/infrastructures/env-var.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import envVar from 'env-var' 3 | 4 | @Injectable() 5 | export class EnvVar { 6 | 7 | private envVar: typeof envVar 8 | 9 | constructor ( 10 | env?: typeof process.env, // <-- testing purpose 11 | ) { 12 | const ev = env ? envVar.from(env) : envVar 13 | this.envVar = ev as any as typeof envVar 14 | } 15 | 16 | get (key: string) { 17 | return this.envVar.get(key) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/infrastructures/infrastructure.module.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sort-keys */ 2 | import { Module } from '@nestjs/common' 3 | import { Brolog } from 'brolog' 4 | import { log } from 'wechaty' 5 | 6 | import { EnvVar } from './env-var.js' 7 | 8 | @Module({ 9 | providers: [ 10 | { 11 | provide: Brolog, 12 | useValue: log, 13 | }, 14 | { 15 | provide: EnvVar, 16 | useValue: new EnvVar(), 17 | }, 18 | ], 19 | exports: [ 20 | Brolog, 21 | EnvVar, 22 | ], 23 | }) 24 | export class InfrastructureModule {} 25 | -------------------------------------------------------------------------------- /src/infrastructures/mod.ts: -------------------------------------------------------------------------------- 1 | export { InfrastructureModule } from './infrastructure.module.js' 2 | export { EnvVar } from './env-var.js' 3 | export { getSetupFinis } from './setup-finis.js' 4 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core' 2 | import { ApplicationModule } from './app.module.js' 3 | 4 | import { VERSION } from './config.js' 5 | 6 | import { EnvVar } from './infrastructures/mod.js' 7 | 8 | async function bootstrap () { 9 | const app = await NestFactory.create(ApplicationModule) 10 | const envVar = await app.resolve(EnvVar) 11 | 12 | const port = envVar 13 | .get('WEB_PORT') 14 | .default(8788) 15 | .asPortNumber() 16 | 17 | console.info(`Friday BOT v${VERSION}`) 18 | 19 | console.info(`Listening on port ${port} ...`) 20 | await app.listen(port, () => 21 | console.info(`Listening on port ${port} ... done`), 22 | ) 23 | } 24 | 25 | bootstrap() 26 | .catch(console.error) 27 | -------------------------------------------------------------------------------- /src/pkg.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | /** 3 | * Wechaty - https://github.com/chatie/wechaty 4 | * 5 | * @copyright 2016-2018 Huan LI 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | import { test } from 'tstest' 22 | 23 | import { pkg } from './pkg.js' 24 | 25 | test('version', async t => { 26 | t.ok(pkg?.version, 'should have VERSION') 27 | t.ok(pkg!.version!.length > 0, 'should not be empty string') 28 | }) 29 | -------------------------------------------------------------------------------- /src/pkg.ts: -------------------------------------------------------------------------------- 1 | import { readPackageUpSync } from 'read-pkg-up' 2 | 3 | import { dirname } from 'path' 4 | import { fileURLToPath } from 'url' 5 | 6 | const __dirname = dirname(fileURLToPath(import.meta.url)) 7 | 8 | const pkg = readPackageUpSync({ cwd: __dirname })?.packageJson 9 | 10 | export { pkg } 11 | -------------------------------------------------------------------------------- /src/presentations/commands/handlers/chatops.handler.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { 3 | CommandBus, 4 | CommandHandler, 5 | ICommandHandler, 6 | } from '@nestjs/cqrs' 7 | 8 | import * as PUPPET from 'wechaty-puppet' 9 | 10 | import { SendMessageCommand } from '../../../wechaty-events/mod.js' 11 | import { WeChatSettings } from '../../../wechaty-settings/mod.js' 12 | import { WechatyRepository } from '../../../wechaty-repository/mod.js' 13 | 14 | import { ChatopsCommand } from '../impls/chatops.command.js' 15 | 16 | @CommandHandler(ChatopsCommand) 17 | export class ChatopsHandler implements ICommandHandler { 18 | 19 | constructor ( 20 | private readonly log: Brolog, 21 | private readonly commandBus: CommandBus, 22 | private readonly settings: WeChatSettings, 23 | private readonly repository: WechatyRepository, 24 | ) {} 25 | 26 | async execute (command: ChatopsCommand) { 27 | this.log.verbose('ChatopsHandler', 'execute({roomId:%s, text:%s})', 28 | command.roomId, 29 | command.text, 30 | ) 31 | 32 | const wechaty = this.repository.findByName('WeChat') 33 | if (!wechaty) { 34 | this.log.warn('ChatopsHandler', 'execute() no wechaty found') 35 | return 36 | } 37 | 38 | const puppetId = wechaty.puppet.id 39 | const roomId = this.settings.rooms.chatops.friday 40 | 41 | await this.commandBus.execute( 42 | new SendMessageCommand( 43 | puppetId, 44 | roomId, 45 | PUPPET.payloads.sayable.text(command.text), 46 | ), 47 | ) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/presentations/commands/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { ChatopsHandler } from './chatops.handler.js' 2 | 3 | export const CommandHandlers = [ 4 | ChatopsHandler, 5 | ] 6 | -------------------------------------------------------------------------------- /src/presentations/commands/impls/chatops.command.ts: -------------------------------------------------------------------------------- 1 | export class ChatopsCommand { 2 | 3 | constructor ( 4 | public readonly roomId: string, 5 | public readonly text: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/presentations/commands/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './chatops.command.js' 2 | -------------------------------------------------------------------------------- /src/presentations/commands/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { CommandHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/presentations/interfaces/chatops-dto.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ChatopsDto { 2 | text: string 3 | } 4 | -------------------------------------------------------------------------------- /src/presentations/interfaces/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './chatops-dto.interface.js' 2 | -------------------------------------------------------------------------------- /src/presentations/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './commands/impls/mod.js' 2 | export { PresentationModule } from './presentation.module.js' 3 | -------------------------------------------------------------------------------- /src/presentations/presentation.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Module, 3 | } from '@nestjs/common' 4 | import { CqrsModule } from '@nestjs/cqrs' 5 | 6 | import { InfrastructureModule } from '../infrastructures/mod.js' 7 | import { WechatyEventsModule } from '../wechaty-events/mod.js' 8 | import { WechatyRepositoryModule } from '../wechaty-repository/mod.js' 9 | import { WechatySettingsModule } from '../wechaty-settings/mod.js' 10 | 11 | import { CommandHandlers } from './commands/mod.js' 12 | 13 | import { PresentationController } from './presentation.controller.js' 14 | 15 | @Module({ 16 | controllers: [ PresentationController ], 17 | imports: [ 18 | CqrsModule, 19 | InfrastructureModule, 20 | WechatyEventsModule, 21 | WechatyRepositoryModule, 22 | WechatySettingsModule, 23 | ], 24 | providers: [ 25 | ...CommandHandlers, 26 | ], 27 | }) 28 | export class PresentationModule {} 29 | -------------------------------------------------------------------------------- /src/pures/mod.ts: -------------------------------------------------------------------------------- 1 | export { stringFilterFactory } from './string-filter.js' 2 | -------------------------------------------------------------------------------- /src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vorpal-hacker-news' 2 | -------------------------------------------------------------------------------- /src/wechaty-events/commands/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | import { SendMessageHandler } from './send-message.handler.js' 2 | 3 | export const CommandHandlers = [ 4 | SendMessageHandler, 5 | ] 6 | -------------------------------------------------------------------------------- /src/wechaty-events/commands/handlers/send-message.handler.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CommandHandler, 3 | ICommandHandler, 4 | } from '@nestjs/cqrs' 5 | import { Brolog } from 'brolog' 6 | 7 | import { WechatyRepository } from '../../../wechaty-repository/mod.js' 8 | 9 | import { SendMessageCommand } from '../impls/mod.js' 10 | 11 | @CommandHandler(SendMessageCommand) 12 | export class SendMessageHandler implements ICommandHandler { 13 | 14 | constructor ( 15 | private readonly log: Brolog, 16 | private readonly repository: WechatyRepository, 17 | ) {} 18 | 19 | async execute (command: SendMessageCommand) { 20 | this.log.verbose('SendMessageHandler', 'execute({puppetId: %s, conversationalId: %s})', 21 | command.puppetId, 22 | command.conversaionId, 23 | ) 24 | 25 | const wechaty = await this.repository.findByPuppetId(command.puppetId) 26 | if (!wechaty) { 27 | this.log.warn('SendMessageHandler', 'execute() no wechaty found for puppetId: %s', command.puppetId) 28 | return 29 | } 30 | 31 | if (!wechaty.isLoggedIn) { 32 | this.log.warn('SendMessageHandler', 'execute() wechaty is not logged in for puppetId: %s', command.puppetId) 33 | return 34 | } 35 | 36 | try { 37 | await wechaty.puppet.messageSend( 38 | command.conversaionId, 39 | command.sayable, 40 | ) 41 | } catch (e) { 42 | this.log.error('SendMessageHandler', 'execute() puppet.messageSend() exception: %s', (e as Error).message) 43 | console.error(e) 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/wechaty-events/commands/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './send-message.command.js' 2 | -------------------------------------------------------------------------------- /src/wechaty-events/commands/impls/send-message.command.ts: -------------------------------------------------------------------------------- 1 | import type * as PUPPET from 'wechaty-puppet' 2 | 3 | export class SendMessageCommand { 4 | 5 | constructor ( 6 | public readonly puppetId: string, 7 | public readonly conversaionId: string, 8 | public readonly sayable: PUPPET.payloads.Sayable, 9 | ) {} 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/wechaty-events/commands/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { CommandHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/wechaty-events/events/handlers/mod.ts: -------------------------------------------------------------------------------- 1 | export const EventHandlers = [ 2 | ] 3 | -------------------------------------------------------------------------------- /src/wechaty-events/events/impls/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './puppet-message-received.event.js' 2 | -------------------------------------------------------------------------------- /src/wechaty-events/events/impls/puppet-message-received.event.ts: -------------------------------------------------------------------------------- 1 | export class PuppetMessageReceivedEvent { 2 | 3 | constructor ( 4 | public readonly puppetId: string, 5 | public readonly messageId: string, 6 | ) {} 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/wechaty-events/events/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './impls/mod.js' 2 | export { EventHandlers } from './handlers/mod.js' 3 | -------------------------------------------------------------------------------- /src/wechaty-events/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './commands/mod.js' 2 | export * from './events/mod.js' 3 | export { WechatyEventsModule } from './wechaty-events.module.js' 4 | -------------------------------------------------------------------------------- /src/wechaty-events/wechaty-events.module.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Module, 3 | } from '@nestjs/common' 4 | 5 | import { InfrastructureModule } from '../infrastructures/mod.js' 6 | import { WechatyRepositoryModule } from '../wechaty-repository/mod.js' 7 | 8 | import { CommandHandlers } from './commands/mod.js' 9 | import { EventHandlers } from './events/mod.js' 10 | 11 | @Module({ 12 | imports: [ 13 | InfrastructureModule, 14 | WechatyRepositoryModule, 15 | ], 16 | providers: [ 17 | ...CommandHandlers, 18 | ...EventHandlers, 19 | ], 20 | }) 21 | export class WechatyEventsModule {} 22 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/builder.ts: -------------------------------------------------------------------------------- 1 | import type * as WECHATY from 'wechaty' 2 | 3 | import type { NamedInterface } from '../../wechaty-settings/mod.js' 4 | 5 | export interface Builder extends WECHATY.BuilderInterface, WECHATY.BuilderInterface { 6 | settings: NamedInterface 7 | } 8 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/gitter/gitter-builder.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { WechatyBuilder } from 'wechaty' 3 | import { Brolog } from 'brolog' 4 | import { PuppetGitter } from 'wechaty-puppet-gitter' 5 | 6 | import { GitterSettings } from '../../../wechaty-settings/mod.js' 7 | 8 | import type { Builder } from '../builder.js' 9 | 10 | import { getPlugins } from './plugins/mod.js' 11 | 12 | @Injectable() 13 | class GitterBuilder implements Builder { 14 | 15 | constructor ( 16 | private readonly log: Brolog, 17 | public readonly settings: GitterSettings, 18 | ) { 19 | this.log.verbose('GitterBuilder', 'constructor({name: %s, token: %s})', 20 | settings.name, 21 | settings.token, 22 | ) 23 | } 24 | 25 | build () { 26 | this.log.verbose('GitterBuilder', 'build()') 27 | 28 | const puppet = new PuppetGitter({ 29 | token: this.settings.token, 30 | }) 31 | 32 | const wechaty = WechatyBuilder.build({ 33 | name: this.settings.name, 34 | puppet, 35 | }) 36 | 37 | wechaty.use(getPlugins()) 38 | 39 | return wechaty 40 | } 41 | 42 | } 43 | 44 | export { 45 | GitterBuilder, 46 | } 47 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/gitter/plugins/mod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wechaty Plugin Support with KickOut Example #1939 3 | * https://github.com/wechaty/wechaty/issues/1939 4 | */ 5 | import { 6 | DingDong, 7 | EventLogger, 8 | } from 'wechaty-plugin-contrib' 9 | 10 | import { vorpalPluginList } from './vorpals/mod.js' 11 | 12 | // import { 13 | // QnAMakerCeibsPlugin, 14 | // } from './qnamaker.js' 15 | 16 | const getPlugins = () => { 17 | 18 | const pluginList = [ 19 | DingDong(), 20 | EventLogger(), 21 | // QnAMakerCeibsPlugin, 22 | ] 23 | return [ 24 | ...pluginList, 25 | ...vorpalPluginList, 26 | ] 27 | } 28 | 29 | export { getPlugins } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/gitter/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | const vorpalPluginList = [] as any 2 | 3 | export { vorpalPluginList } 4 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/mod.ts: -------------------------------------------------------------------------------- 1 | import { GitterBuilder } from './gitter/gitter-builder.js' 2 | import { OABuilder } from './oa/oa-builder.js' 3 | import { QqBuilder } from './qq/qq-builder.js' 4 | import { WeChatBuilder } from './wechat/wechat-builder.js' 5 | import { WhatsAppBuilder } from './whatsapp/whatsapp-builder.js' 6 | import { WorkProBuilder } from './workpro/workpro-builder.js' 7 | 8 | const WechatyBuilders = [ 9 | GitterBuilder, 10 | OABuilder, 11 | QqBuilder, 12 | WeChatBuilder, 13 | WhatsAppBuilder, 14 | WorkProBuilder, 15 | ] 16 | 17 | export { 18 | WechatyBuilders, 19 | GitterBuilder, 20 | OABuilder, 21 | QqBuilder, 22 | WeChatBuilder, 23 | WhatsAppBuilder, 24 | WorkProBuilder, 25 | } 26 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/oa-builder.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { 3 | WechatyBuilder, 4 | } from 'wechaty' 5 | import { Brolog } from 'brolog' 6 | 7 | import { PuppetOA } from 'wechaty-puppet-official-account' 8 | 9 | import { OaSettings } from '../../../wechaty-settings/mod.js' 10 | 11 | import { getPlugins } from './plugins/mod.js' 12 | import type { Builder } from '../builder.js' 13 | 14 | @Injectable() 15 | class OABuilder implements Builder { 16 | 17 | constructor ( 18 | private readonly log: Brolog, 19 | public readonly settings: OaSettings, 20 | ) { 21 | this.log.verbose('OABuilder', 'constructor({name: %s, webhookProxyUrl: %s})', 22 | settings.name, 23 | settings.webhookProxyUrl, 24 | ) 25 | } 26 | 27 | build () { 28 | this.log.verbose('OABuilder', 'build()') 29 | 30 | const puppet = new PuppetOA({ 31 | appId : this.settings.appId, 32 | appSecret : this.settings.appSecret, 33 | token : this.settings.token, 34 | webhookProxyUrl : this.settings.webhookProxyUrl, 35 | }) 36 | 37 | const wechaty = WechatyBuilder.build({ 38 | name: this.settings.name, 39 | puppet, 40 | }) 41 | 42 | wechaty.use(getPlugins()) 43 | 44 | return wechaty 45 | } 46 | 47 | } 48 | 49 | export { 50 | OABuilder, 51 | } 52 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/plugins/mod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wechaty Plugin Support with KickOut Example #1939 3 | * https://github.com/wechaty/wechaty/issues/1939 4 | */ 5 | import { 6 | DingDong, 7 | } from 'wechaty-plugin-contrib' 8 | 9 | // import { 10 | // QnAMakerCeibsPlugin, 11 | // } from './qnamaker.js' 12 | 13 | import { vorpalPluginList } from './vorpals/mod.js' 14 | 15 | const getPlugins = () => { 16 | const pluginList = [ 17 | DingDong(), 18 | // QnAMakerCeibsPlugin, 19 | ] 20 | 21 | return [ 22 | ...pluginList, 23 | ...vorpalPluginList, 24 | ] 25 | } 26 | 27 | export { 28 | getPlugins, 29 | } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/plugins/qnamaker.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyQnAMaker, 4 | // WechatyQnAMakerConfig, 5 | // } from 'wechaty-qnamaker' 6 | // import { getEnvVar } from '../../../wechaty-settings/deprecated.js' 7 | 8 | // const skipMessage = [ 9 | // /^wechaty$/i, 10 | // ] 11 | 12 | // const scoreThreshold = 10 13 | 14 | // const configCeibs: WechatyQnAMakerConfig = { 15 | // language: ['chinese'], 16 | 17 | // skipMessage, 18 | // contact: true, 19 | // scoreThreshold, 20 | 21 | // endpointKey : getEnvVar().get('WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY_CEIBS').asString(), 22 | // knowledgeBaseId : getEnvVar().get('WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID_CEIBS').asString(), 23 | // resourceName : getEnvVar().get('WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME_CEIBS').asString(), 24 | // } 25 | 26 | // const QnAMakerCeibsPlugin = WechatyQnAMaker(configCeibs) 27 | 28 | // export { 29 | // QnAMakerCeibsPlugin, 30 | // // configCeibs, 31 | // } 32 | export {} 33 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/plugins/vorpals/contrib.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ding, 7 | MathMaster, 8 | Version, 9 | Whoru, 10 | } from 'wechaty-vorpal-contrib' 11 | 12 | const chatopsConfig: WechatyVorpalConfig = { 13 | contact : true, 14 | silent : true, 15 | 16 | use: [ 17 | Ding(), 18 | MathMaster(), 19 | Whoru(), 20 | Version(), 21 | ], 22 | } 23 | 24 | const ChatopsVorpalPlugin = WechatyVorpal(chatopsConfig) 25 | 26 | export { 27 | ChatopsVorpalPlugin, 28 | } 29 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | import * as contrib from './contrib.js' 2 | import * as faq from './qnamaker.js' 3 | 4 | const vorpalPluginList = [ 5 | ...Object.values(contrib), 6 | ...Object.values(faq), 7 | ] 8 | 9 | export { vorpalPluginList } 10 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/oa/plugins/vorpals/qnamaker.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | // import { Faq } from 'wechaty-qnamaker' 6 | 7 | // import { configCeibs } from '../qnamaker.js' 8 | 9 | const faqConfig: WechatyVorpalConfig = { 10 | contact : true, 11 | mention : true, 12 | room : true, 13 | silent : true, 14 | 15 | use: [ 16 | // Faq(configCeibs), 17 | ], 18 | } 19 | 20 | const FaqVorpalPlugin = WechatyVorpal(faqConfig) 21 | 22 | export { 23 | FaqVorpalPlugin, 24 | } 25 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/qq/plugins/mod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wechaty Plugin Support with KickOut Example #1939 3 | * https://github.com/wechaty/wechaty/issues/1939 4 | */ 5 | import { 6 | DingDong, 7 | EventLogger, 8 | } from 'wechaty-plugin-contrib' 9 | import { getVorpalPlugins } from './vorpals/mod.js' 10 | 11 | // import { 12 | // QnAMakerCeibsPlugin, 13 | // } from './qnamaker.js' 14 | 15 | const getPlugins = () => { 16 | const pluginList = [ 17 | DingDong(), 18 | EventLogger(), 19 | // QnAMakerCeibsPlugin, 20 | ] 21 | const vorpalPluginList = getVorpalPlugins() 22 | 23 | return [ 24 | ...pluginList, 25 | ...vorpalPluginList, 26 | ] 27 | } 28 | 29 | export { getPlugins } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/qq/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | const getVorpalPlugins = () => { 2 | const vorpalPluginList = [] as any 3 | return vorpalPluginList 4 | } 5 | 6 | export { getVorpalPlugins } 7 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/qq/qq-builder.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import * as WECHATY from 'wechaty' 3 | import { Brolog } from 'brolog' 4 | 5 | import { PuppetOICQ } from 'wechaty-puppet-oicq' 6 | 7 | import { getPlugins } from './plugins/mod.js' 8 | import { QqSettings } from '../../../wechaty-settings/mod.js' 9 | import type { Builder } from '../builder.js' 10 | 11 | @Injectable() 12 | export class QqBuilder implements Builder { 13 | 14 | constructor ( 15 | private readonly log: Brolog, 16 | public readonly settings: QqSettings, 17 | ) { 18 | this.log.verbose('OicqBuilder', 'constructor({name: %s, qq: %s})', 19 | settings.name, 20 | settings.qq, 21 | ) 22 | } 23 | 24 | build () { 25 | this.log.verbose('OicqBuilder', 'build()') 26 | 27 | const puppet = new PuppetOICQ({ qq: this.settings.qq }) 28 | 29 | const wechaty = WECHATY.WechatyBuilder.build({ 30 | name: this.settings.name, 31 | puppet, 32 | }) 33 | 34 | wechaty.use(getPlugins()) 35 | 36 | return wechaty 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/get-io-client.ts: -------------------------------------------------------------------------------- 1 | // import { 2 | // Wechaty, 3 | // log, 4 | // IoClient, 5 | // } from 'wechaty' 6 | // import type { 7 | // IoClientOptions, 8 | // } from 'wechaty/dist/esm/src/io-client.js' 9 | 10 | // import type { WeChatSettings } from '../../../wechaty-settings/mod' 11 | 12 | // function getIoClient ( 13 | // wechaty: Wechaty, 14 | // settings: WeChatSettings, 15 | // ) { 16 | // log.verbose('getWechaty', 'getIoClient(%s)', wechaty) 17 | 18 | // const token = settings.wechatyToken 19 | // const port = settings.wechatyPuppetServerPort 20 | 21 | // if (!port) { 22 | // throw new Error('port not found: please set WECHATY_PUPPET_SERVER_PORT in environment before start') 23 | // } 24 | 25 | // const options: IoClientOptions = { 26 | // port, 27 | // token, 28 | // wechaty, 29 | // } 30 | 31 | // const client = new IoClient(options) 32 | 33 | // return client 34 | // } 35 | 36 | // export { getIoClient } 37 | export {} 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/get-memory.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | } from 'wechaty' 4 | import { 5 | MemoryCard, 6 | } from 'memory-card' 7 | import type { EnvVar } from '../../../infrastructures/mod.js' 8 | 9 | export let memory: undefined | MemoryCard 10 | 11 | export function getMemory ( 12 | name: string, 13 | envVar: EnvVar, 14 | ): MemoryCard { 15 | log.verbose('getMemory', 'getMemory(%s)', name) 16 | 17 | if (memory) { 18 | return memory 19 | } 20 | 21 | const AWS_ACCESS_KEY_ID = envVar.get('AWS_ACCESS_KEY_ID').asString() 22 | const AWS_SECRET_ACCESS_KEY = envVar.get('AWS_SECRET_ACCESS_KEY').asString() 23 | const AWS_REGION = envVar.get('AWS_REGION').asString() 24 | const AWS_S3_BUCKET = envVar.get('AWS_S3_BUCKET').asString() 25 | 26 | if (AWS_ACCESS_KEY_ID 27 | && AWS_REGION 28 | && AWS_SECRET_ACCESS_KEY 29 | && AWS_S3_BUCKET 30 | ) { 31 | log.verbose('getMemory', 'getMemory() creating new s3 memory') 32 | 33 | memory = new MemoryCard({ 34 | name, 35 | storageOptions: { 36 | accessKeyId : AWS_ACCESS_KEY_ID, 37 | bucket : AWS_S3_BUCKET, 38 | region : AWS_REGION, 39 | secretAccessKey : AWS_SECRET_ACCESS_KEY, 40 | type : 's3', 41 | }, 42 | }) 43 | } else { 44 | log.verbose('getMemory', 'getMemory() creating new file memory') 45 | memory = new MemoryCard({ name }) 46 | } 47 | 48 | return memory 49 | } 50 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-error.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Wechaty, 4 | } from 'wechaty' 5 | 6 | export default async function onError ( 7 | this : Wechaty, 8 | e : Error, 9 | ): Promise { 10 | log.error('on-error', 'onError(%s)', e) 11 | console.error(e) 12 | console.error(e.stack) 13 | } 14 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-friendship.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Friendship, 4 | Wechaty, 5 | } from 'wechaty' 6 | import type { WeChatSettings } from '../../../../wechaty-settings/mod.js' 7 | 8 | const getOnFriendship = (settings: WeChatSettings) => { 9 | return async function onFriendship ( 10 | this : Wechaty, 11 | friendship : Friendship, 12 | ): Promise { 13 | log.info('on-friendship', 'onFriendship(%s)', friendship) 14 | 15 | const contact = await friendship.contact() 16 | const hello = await friendship.hello() 17 | 18 | let text: string 19 | if (friendship.type() === this.Friendship.Type.Receive) { 20 | text = `received friendship from ${contact} with ${hello}` 21 | } else if (friendship.type() === this.Friendship.Type.Confirm) { 22 | text = `confirmed friendship from ${contact}` 23 | } else { 24 | throw new Error('unknown friendship type: ' + friendship.type()) 25 | } 26 | 27 | const room = await this.Room.find({ id: settings.rooms.chatops.friday }) 28 | if (!room) { 29 | throw new Error('room id: ' + settings.rooms.chatops.friday + ' not found') 30 | } 31 | await room.say(text) 32 | } 33 | } 34 | 35 | export { 36 | getOnFriendship, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-login.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | VERSION, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onLogin ( 9 | this : Wechaty, 10 | user : Contact, 11 | ): Promise { 12 | const msg = `${user.name()} BOT5 Wechaty@${VERSION} logged in` 13 | log.info('startBot', 'onLogin(%s) %s', user, msg) 14 | await user.say(msg) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-logout.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Wechaty, 5 | } from 'wechaty' 6 | 7 | export default async function onLogout ( 8 | this : Wechaty, 9 | user : Contact, 10 | ): Promise { 11 | log.info('on-logout', 'onLogout(%s)', user) 12 | } 13 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-room-invite.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | RoomInvitation, 4 | Wechaty, 5 | } from 'wechaty' 6 | 7 | export default async function onRoomInvite ( 8 | this : Wechaty, 9 | roomInvitation : RoomInvitation, 10 | ): Promise { 11 | log.info('on-room-invite', 'onRoomInvite(%s)', roomInvitation) 12 | } 13 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-room-join.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomJoin ( 9 | this : Wechaty, 10 | room : Room, 11 | inviteeList : Contact[], 12 | inviter : Contact, 13 | ): Promise { 14 | log.info('on-room-join', 'onRoomJoin(%s, %s, %s)', room, inviteeList.join(','), inviter) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-room-leave.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomLeave ( 9 | this : Wechaty, 10 | room : Room, 11 | leaverList : Contact[], 12 | remover? : Contact, 13 | ): Promise { 14 | log.info('on-room-leave', 'onRoomLeave(%s, %s, %s)', room, leaverList.join(','), remover) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-room-topic.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomTopic ( 9 | this : Wechaty, 10 | room : Room, 11 | newTopic : string, 12 | oldTopic : string, 13 | changer : Contact, 14 | ): Promise { 15 | log.info('startBot', 'onRoomTopic(%s, %s, %s, %s)', room, newTopic, oldTopic, changer) 16 | } 17 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/handlers/on-scan.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Wechaty, 4 | } from 'wechaty' 5 | import { 6 | ScanStatus, 7 | } from 'wechaty-puppet/types' 8 | 9 | export default async function onScan ( 10 | this : Wechaty, 11 | qrcode : string, 12 | status : number, 13 | ): Promise { 14 | log.info('on-scan', 'onScan() [%s(%s)] %s', 15 | ScanStatus[status], 16 | status, 17 | qrcode, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/bot5-assistant.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Bot5Assistant, 3 | } from 'wechaty-bot5-assistant' 4 | import type { WeChatSettings } from '../../../../wechaty-settings/mod.js' 5 | 6 | const getBot5AssistantPlugin = (settings: WeChatSettings) => { 7 | const [ _next, current, ..._previous ] = settings.rooms.bot5Club.rooms 8 | 9 | const Bot5AssistantPlugin = Bot5Assistant({ 10 | room: [ 11 | current, 12 | /^BOT5 Assistant/i, // room topic 13 | ], 14 | }) 15 | return Bot5AssistantPlugin 16 | } 17 | 18 | export { 19 | getBot5AssistantPlugin, 20 | } 21 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/chatops.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChatOps, 3 | } from 'wechaty-plugin-contrib' 4 | import type { WeChatSettings } from '../../../../wechaty-settings/mod.js' 5 | 6 | const getChatOpsPlugin = (settings: WeChatSettings) => { 7 | const ChatOpsPlugin = ChatOps({ 8 | blacklist: [ 9 | 'bot-sentry', 10 | ], 11 | room: settings.rooms.chatops.friday, 12 | }) 13 | return ChatOpsPlugin 14 | } 15 | 16 | export { 17 | getChatOpsPlugin, 18 | } 19 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/crontab.ts: -------------------------------------------------------------------------------- 1 | 2 | import cron from 'node-cron' 3 | 4 | interface CRONConfig { 5 | time: string, 6 | reply: string 7 | } 8 | 9 | const CRON_CONFIG: CRONConfig[] = [ 10 | { 11 | reply: '星期一了,如果主席还没发活动总结的话要注意了', 12 | /** 13 | * 定时任务 14 | * ┌─────────────── second (optional) 15 | * │ ┌───────────── minute 16 | * │ │ ┌─────────── hour 17 | * │ │ │ ┌──────── day of month 18 | * │ │ │ │ ┌────── month 19 | * │ │ │ │ │ ┌──── day of week 20 | * │ │ │ │ │ │ 21 | * │ │ │ │ │ │ 22 | * * * * * * * // */ 23 | time: '0 0 19 * * 1', 24 | }, 25 | ] 26 | 27 | /** 28 | * TODO: Huan(202006) 29 | */ 30 | export async function crontab () { 31 | for (const cronConfig of CRON_CONFIG) { 32 | cron.schedule(cronConfig.time, () => { 33 | // await Chatops.instance().say(cronConfig.reply) 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/ding-dong/mod.ts: -------------------------------------------------------------------------------- 1 | import { DingDong } from 'wechaty-plugin-contrib' 2 | 3 | const DEFAULT_CONFIG = { 4 | contact : true, 5 | room : false, 6 | } 7 | 8 | const wechatyDingDongConfig = { 9 | ding: [ 10 | /^wechaty$/i, 11 | /^(\w+)*\s*wechaty\s*(\w+)*$/i, 12 | ], 13 | dong: [ 14 | "Welcome to join our Wechaty Developers' Home by visiting https://gitter.im/wechaty/wechaty", 15 | 'Currently, the WeChat room invitation is temporary not available because we are experiencing issue #62, learn more from https://github.com/wechaty/friday/issues/62 if you are interested.', 16 | 'If you want to join the WeChat room, please come back and send me "Wechaty" again after December 3rd.', 17 | ], 18 | } 19 | 20 | const WechatyDingDongPlugin = DingDong({ 21 | ...DEFAULT_CONFIG, 22 | ...wechatyDingDongConfig, 23 | }) 24 | 25 | export { 26 | WechatyDingDongPlugin, 27 | } 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/freshdesk.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyFreshdesk, 4 | // WechatyFreshdeskConfig, 5 | // } from 'wechaty-freshdesk' 6 | 7 | // const config: WechatyFreshdeskConfig = { 8 | // close: [ 9 | // [ 10 | // 'Thank you for contacting the support of puppet service provider.', 11 | // 'We believe that your question has been answered and hope you are good now.', 12 | // 'Please feel free to contact us again if you have more questions.', 13 | // 'Thank you very much, and have a nice day!', 14 | // ].join(' '), 15 | // ], 16 | // contact: true, 17 | // room: false, 18 | 19 | // /** 20 | // * Huan(202201) 21 | // * TODO: use FridayConfig for the environment variables 22 | // */ 23 | // apiKey : process.env['WECHATY_PLUGIN_FRESHDESK_API_KEY'], 24 | // portalUrl : process.env['WECHATY_PLUGIN_FRESHDESK_PORTAL_URL'], 25 | // webhookProxyUrl : process.env['WECHATY_PLUGIN_FRESHDESK_WEBHOOK_PROXY_URL'], 26 | // } 27 | 28 | // export const FreshdeskPlugin = WechatyFreshdesk(config) 29 | export {} 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/friendship-accepter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FriendshipAccepter, 3 | FriendshipAccepterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | 6 | const NEW_FRIEND_GREETING = [ 7 | "Hi there, my name is Friday, I'm a Chatbot built by Wechaty and for Wechaty.", 8 | "I'm open-sourced, please feel free to visit https://github.com/wechaty/friday if you want to know me more.", 9 | "Currently, I'm in charge of managing some developers' WeChat group: if you know any secret words, please send to me, then I'll be able to invite you to join!", 10 | ] 11 | 12 | const config: FriendshipAccepterConfig = { 13 | greeting: NEW_FRIEND_GREETING, 14 | // keyword: '42', 15 | } 16 | 17 | export const FriendshipAccepterPlugin = FriendshipAccepter(config) 18 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/heartbeat.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Heartbeat, 3 | HeartbeatConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../wechaty-settings/mod.js' 6 | 7 | const getHeartbeatPlugin = (settings: WeChatSettings) => { 8 | const config: HeartbeatConfig = { 9 | emoji: { 10 | heartbeat : '[爱心]', 11 | login : '[太阳]', 12 | logout : '[月亮]', 13 | ready : '[拳头]', 14 | }, 15 | intervalSeconds: 60 * 60, // 1 hour 16 | room: settings.rooms.chatops.heartbeat, 17 | } 18 | 19 | const HeartbeatPlugin = Heartbeat(config) 20 | return HeartbeatPlugin 21 | } 22 | 23 | export { 24 | getHeartbeatPlugin, 25 | } 26 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/intercom.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyIntercom, 4 | // WechatyIntercomConfig, 5 | // } from 'wechaty-intercom' 6 | 7 | // const config: WechatyIntercomConfig = { 8 | // mention: true, 9 | // close: [ 10 | // [ 11 | // 'Thank you for contacting the support of puppet service provider.', 12 | // 'We believe that your question has been answered and hope you are good now.', 13 | // 'Please feel free to contact us again if you have more questions.', 14 | // 'Thank you very much, and have a nice day!', 15 | // ].join(' '), 16 | // ], 17 | // room: false, 18 | 19 | // /** 20 | // * Huan(202201): TODO: use FridayConfig for the environment variables 21 | // */ 22 | // intercomToken : process.env['WECHATY_PLUGIN_INTERCOM_TOKEN'], 23 | // webhookProxyUrl : process.env['WECHATY_PLUGIN_INTERCOM_WEBHOOK_PROXY_URL'], 24 | // } 25 | 26 | // export const IntercomPlugin = WechatyIntercom(config) 27 | export {} 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/qnamaker.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyQnAMaker, 4 | // WechatyQnAMakerConfig, 5 | // } from 'wechaty-qnamaker' 6 | 7 | // import { botSettings } from '../../../wechaty-settings/deprecated.js' 8 | 9 | // const skipMessage = [ 10 | // /^wechaty$/i, 11 | // ] 12 | 13 | // const room = [ 14 | // ...botSettings.weChat.rooms.wechatyDevelopers.home, // DEVELOPERS_ROOM_ID_LIST, 15 | // ...Object.values(botSettings.weChat.rooms.wechatyUserGroup).flat(), // MULTI_LANG_ROOM_ID, 16 | // ] 17 | 18 | // const scoreThreshold = 30 19 | 20 | // const options = { 21 | // contact: true, 22 | // mention: true, 23 | // room, 24 | // scoreThreshold, 25 | // skipMessage, 26 | // } 27 | 28 | // /** 29 | // * Huan(202201): TODO: use `FridayConfig` for the environment variables 30 | // */ 31 | // export const configEnglish: WechatyQnAMakerConfig = { 32 | // ...options, 33 | // language : ['english'], 34 | // endpointKey : process.env['WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY'], 35 | // knowledgeBaseId : process.env['WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID'], 36 | // resourceName : process.env['WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME'], 37 | // } 38 | 39 | // export const configChinese: WechatyQnAMakerConfig = { 40 | // ...options, 41 | // language : ['chinese'], 42 | // endpointKey : process.env['WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY_CHINESE'], 43 | // knowledgeBaseId : process.env['WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID_CHINESE'], 44 | // resourceName : process.env['WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME_CHINESE'], 45 | // } 46 | 47 | // const QnAMakerEnglishPlugin = WechatyQnAMaker(configEnglish) 48 | // const QnAMakerChinesePlugin = WechatyQnAMaker(configChinese) 49 | 50 | // export { 51 | // QnAMakerChinesePlugin, 52 | // QnAMakerEnglishPlugin, 53 | // } 54 | export {} 55 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/abbr-room-topic-by-regex.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | /** 3 | * Wechaty - https://github.com/chatie/wechaty 4 | * 5 | * @copyright 2016-2018 Huan LI 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | import { test } from 'tstest' 22 | import type { Message } from 'wechaty' 23 | 24 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 25 | 26 | test('abbrRoomTopicForAll()', async t => { 27 | const FIXTURES = [ 28 | [ "Wechaty Developers' Home 8", 'Home 8' ], 29 | [ "Wechaty Developers' Home", 'Home' ], 30 | [ 'Python Wechaty User Group', 'Python' ], 31 | [ 'Wechaty Broadcast Station', 'Station' ], 32 | [ 'BOT5 Club Open Forum - BFOF 2021', 'BOT5' ], 33 | [ 'BOT5 Club Alumni - 2022', 'BOT5' ], 34 | [ 'Wechaty Puppet WhatsApp', 'WhatsApp' ], 35 | ] 36 | 37 | for (const [ topic, expected ] of FIXTURES as [string, string][]) { 38 | const actual = await abbrRoomTopicForAll({ 39 | room: () => ({ 40 | topic: () => topic, 41 | }), 42 | } as any as Message) 43 | t.equal(actual, expected, `should convert "${topic}" => "${expected}"`) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/abbr-room-topic-by-regex.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from 'wechaty' 2 | 3 | function abbrRoomTopicByRegex (matcher: RegExp) { 4 | return async function abbrRoomTopic (message: Message): Promise { 5 | const room = message.room() 6 | if (!room) { 7 | return 8 | } 9 | 10 | const topic = await room.topic() 11 | const matched = topic.match(matcher) 12 | 13 | if (!matched) { 14 | return 15 | } 16 | 17 | return matched[1] 18 | } 19 | } 20 | 21 | /** 22 | * "Wechaty Developers' Home 8" -> "Home 8" 23 | */ 24 | const abbrRoomTopicForDevelopersHome = abbrRoomTopicByRegex(/^Wechaty\s+.*?([^\s]+\s*(\d+)?)$/i) 25 | /** 26 | * "Python Wechaty User Group" -> "Python" 27 | */ 28 | const abbrRoomTopicForPolyglot = abbrRoomTopicByRegex(/^\s*([^\s]+)\s+Wechaty User Group$/i) 29 | 30 | /** 31 | * "Wechaty Puppet WhatsApp" -> "WhatsApp" 32 | */ 33 | const abbrRoomTopicForPuppet = abbrRoomTopicByRegex(/^Wechaty Puppet ([^\s]+)$/i) 34 | 35 | const abbrRoomTopicForBot5 = abbrRoomTopicByRegex(/^(BOT5) /) 36 | 37 | const abbrRoomTopicForAll = async (message: Message) => { 38 | return await abbrRoomTopicForDevelopersHome(message) 39 | || await abbrRoomTopicForPolyglot(message) 40 | || await abbrRoomTopicForPuppet(message) 41 | || await abbrRoomTopicForBot5(message) 42 | } 43 | 44 | export { 45 | abbrRoomTopicByRegex, 46 | abbrRoomTopicForDevelopersHome, 47 | abbrRoomTopicForAll, 48 | } 49 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/bidirectional-mapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | // import { 6 | // mappers, 7 | // } from 'wechaty-plugin-contrib' 8 | 9 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 10 | import { senderDisplayName } from './sender-display-name.js' 11 | 12 | const bidirectionalMapper = async (message: Message) => { 13 | // Drop all messages if not Text 14 | if (message.type() !== types.Message.Text) { return } 15 | 16 | const talkerDisplayName = await senderDisplayName(message) 17 | const roomShortName = await abbrRoomTopicForAll(message) || 'Nowhere' 18 | 19 | const text = message.text() 20 | 21 | return `[${talkerDisplayName}@${roomShortName}]: ${text}` 22 | } 23 | 24 | export { bidirectionalMapper } 25 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/bot5-club/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WeChatSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * BOT5 Club 12 | * 13 | */ 14 | const getBot5OneToManyPlugin = (settings: WeChatSettings) => { 15 | /** 16 | * - first club room in the array is the latest room (current year) 17 | * - second club room in the array (and all the following) 18 | * is the previous room (previous year). 19 | */ 20 | const [ next, current, ...previous ] = settings.rooms.bot5Club.rooms 21 | 22 | const Bot5OneToManyPlugin = SourceToTargetRoomConnector({ 23 | map: getUnidirectionalMapper(settings), 24 | source: [ 25 | next, 26 | current, 27 | ], 28 | target: [ 29 | ...previous, 30 | ], 31 | }) 32 | return Bot5OneToManyPlugin 33 | } 34 | 35 | export { 36 | getBot5OneToManyPlugin, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './bot5-club/mod.js' 2 | export * from './wechaty-developers-home/mod.js' 3 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/sender-display-name.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from 'wechaty' 2 | import { log } from 'wechaty-puppet' 3 | 4 | const senderDisplayName = async (message: Message) => { 5 | const room = message.room() 6 | 7 | let displayName 8 | try { 9 | const talker = message.talker() 10 | const alias = await room?.alias(talker) 11 | displayName = alias || talker.name() || 'Noname' 12 | } catch (e) { 13 | log.error('senderDisplayName', 'message.talker() exception: %s', (e as Error).message) 14 | displayName = 'unknown' 15 | } 16 | 17 | return displayName 18 | } 19 | 20 | export { senderDisplayName } 21 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/unidirectional-mapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 8 | import { senderDisplayName } from './sender-display-name.js' 9 | 10 | const getUnidirectionalMapper = (settings: WeChatSettings) => { 11 | const skipRoomList = [ 12 | ...settings.rooms.wechatyDevelopers.homeHq, 13 | ...settings.rooms.wechatyDevelopers.broadcastStation, 14 | ] 15 | /** 16 | * 17 | * Message Mapper for Room Connectors 18 | * 19 | */ 20 | const unidirectionalMapper = async (message: Message) => { 21 | const talkerDisplayName = await senderDisplayName(message) 22 | const roomShortName = await abbrRoomTopicForAll(message) || 'Nowhere' 23 | 24 | const prefix = `[${talkerDisplayName}@${roomShortName}]` 25 | 26 | const messageList: (string | Message)[] = [] 27 | 28 | switch (message.type()) { 29 | case types.Message.Text: 30 | messageList.push(`${prefix}: ${message.text()}`) 31 | break 32 | 33 | default: // Forward all non-Text messages 34 | messageList.push(message) 35 | { 36 | const room = message.room() 37 | /** 38 | * If message is not sent from Headquarters Room, 39 | * then we add a sender information for the destination rooms. 40 | */ 41 | if (room && !skipRoomList.includes(room.id)) { 42 | const msgType = types.Message[message.type()] 43 | messageList.unshift(`${prefix}: ${msgType}`) 44 | } 45 | } 46 | break 47 | } 48 | 49 | return messageList 50 | } 51 | return unidirectionalMapper 52 | } 53 | 54 | export { getUnidirectionalMapper } 55 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/broadcast-station.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WeChatSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * Announcement 12 | * 13 | */ 14 | const getHeadquartersBroadcastStationPlugin = (settings: WeChatSettings) => { 15 | const exceptBroadcastStation = (roomId: string) => !settings.rooms.wechatyDevelopers.broadcastStation.includes(roomId) 16 | 17 | const allUserGroups = [ 18 | ...Object.values(settings.rooms.polyglotUserGroup).flat(), 19 | ...Object.values(settings.rooms.puppetUserGroup).flat(), 20 | ] 21 | const allHomes = Object.values(settings.rooms.wechatyDevelopers).flat() 22 | .filter(exceptBroadcastStation) 23 | 24 | const HeadquartersBroadcastStationPlugin = SourceToTargetRoomConnector({ 25 | map: getUnidirectionalMapper(settings), 26 | source: [ 27 | ...settings.rooms.wechatyDevelopers.broadcastStation, 28 | ], 29 | target: [ 30 | ...allUserGroups, 31 | ...allHomes, 32 | ], 33 | }) 34 | return HeadquartersBroadcastStationPlugin 35 | } 36 | 37 | export { 38 | getHeadquartersBroadcastStationPlugin, 39 | } 40 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/home-hq.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WeChatSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * Collecting all messages from developers' home X 12 | * 13 | */ 14 | const getHomeHqCollectingPlugin = (settings: WeChatSettings) => { 15 | const HomeHqCollectingPlugin = SourceToTargetRoomConnector({ 16 | blacklist: [ 17 | settings.mikeId, 18 | ], 19 | map: getUnidirectionalMapper(settings), 20 | source: [ 21 | ...settings.rooms.wechatyDevelopers.home, 22 | ], 23 | target: [ 24 | ...settings.rooms.wechatyDevelopers.homeHq, 25 | ], 26 | }) 27 | return HomeHqCollectingPlugin 28 | } 29 | 30 | /** 31 | * 32 | * Broadcasting text and url link from home HQ to developers' home X 33 | * 34 | */ 35 | const getHomeHqAnnouncingPlugin = (settings: WeChatSettings) => { 36 | const HomeHqAnnouncingPlugin = SourceToTargetRoomConnector({ 37 | blacklist: [ 38 | settings.mikeId, 39 | ], 40 | map: getUnidirectionalMapper(settings), 41 | source: [ 42 | ...settings.rooms.wechatyDevelopers.homeHq, 43 | ], 44 | target: [ 45 | ...settings.rooms.wechatyDevelopers.home, 46 | ...settings.rooms.wechatyDevelopers.monitor, 47 | ], 48 | }) 49 | return HomeHqAnnouncingPlugin 50 | } 51 | 52 | export { 53 | getHomeHqAnnouncingPlugin, 54 | getHomeHqCollectingPlugin, 55 | } 56 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/home-mixer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | import { 6 | ManyToManyRoomConnector, 7 | } from 'wechaty-plugin-contrib' 8 | 9 | import type { WeChatSettings } from '../../../../../../wechaty-settings/mod.js' 10 | 11 | import { bidirectionalMapper } from '../bidirectional-mapper.js' 12 | 13 | /** 14 | * 15 | * Many to Many 16 | * 17 | */ 18 | const getManyToManyPlugin = (settings: WeChatSettings) => { 19 | const blacklist = [ 20 | async (message: Message) => message.type() !== types.Message.Text, 21 | settings.mikeId, 22 | ] 23 | 24 | const ManyToManyPlugin = ManyToManyRoomConnector({ 25 | blacklist, 26 | many: [ 27 | ...settings.rooms.wechatyDevelopers.home, 28 | ], 29 | map: bidirectionalMapper, 30 | }) 31 | return ManyToManyPlugin 32 | } 33 | 34 | export { 35 | getManyToManyPlugin, 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/language-filtered/languages-to-homes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WeChatSettings } from '../../../../../../../wechaty-settings/mod.js' 6 | 7 | import { bidirectionalMapper } from '../../bidirectional-mapper.js' 8 | 9 | const getLanguageToHomePlugin = (settings: WeChatSettings) => { 10 | const LanguageToHomePlugin = SourceToTargetRoomConnector({ 11 | blacklist: [ 12 | settings.mikeId, 13 | ], 14 | map: bidirectionalMapper, 15 | source: [ 16 | ...settings.rooms.wechatyDevelopers.chinese, 17 | ...settings.rooms.wechatyDevelopers.english, 18 | ], 19 | target: [ 20 | ...settings.rooms.wechatyDevelopers.home, 21 | ...settings.rooms.wechatyDevelopers.homeHq, 22 | ], 23 | }) 24 | return LanguageToHomePlugin 25 | } 26 | 27 | export { 28 | getLanguageToHomePlugin, 29 | } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/language-filtered/mod.ts: -------------------------------------------------------------------------------- 1 | export { getLanguageToHomePlugin } from './languages-to-homes.js' 2 | export { getHomeToChinesePlugin } from './homes-to-chinese.js' 3 | export { getHomeToEnglishPlugin } from './homes-to-english.js' 4 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/mod.ts: -------------------------------------------------------------------------------- 1 | export { getManyToManyPlugin } from './home-mixer.js' 2 | export { 3 | getHomeHqAnnouncingPlugin, 4 | getHomeHqCollectingPlugin, 5 | } from './home-hq.js' 6 | export { getHeadquartersBroadcastStationPlugin } from './broadcast-station.js' 7 | export { getSourceToTargetPlugin } from './polyglot-to-homes.js' 8 | 9 | export { 10 | getHomeToChinesePlugin, 11 | getHomeToEnglishPlugin, 12 | getLanguageToHomePlugin, 13 | } from './language-filtered/mod.js' 14 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-connectors/wechaty-developers-home/polyglot-to-homes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | import type { WeChatSettings } from '../../../../../../wechaty-settings/mod.js' 5 | 6 | import { bidirectionalMapper } from '../bidirectional-mapper.js' 7 | 8 | /** 9 | * 10 | * OneToMany 11 | * 12 | */ 13 | const getSourceToTargetPlugin = (settings: WeChatSettings) => { 14 | const SourceToTargetPlugin = SourceToTargetRoomConnector({ 15 | map: bidirectionalMapper, 16 | source: [ 17 | ...Object.values(settings.rooms.polyglotUserGroup).flat(), 18 | ...Object.values(settings.rooms.puppetUserGroup).flat(), 19 | ], 20 | target: [ 21 | ...settings.rooms.wechatyDevelopers.homeHq, 22 | ...settings.rooms.wechatyDevelopers.home, 23 | ], 24 | }) 25 | return SourceToTargetPlugin 26 | } 27 | 28 | export { 29 | getSourceToTargetPlugin, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-invitation-accepter.ts: -------------------------------------------------------------------------------- 1 | import { RoomInvitationAccepter } from 'wechaty-plugin-contrib' 2 | 3 | export const RoomInvitationAccepterPlugin = RoomInvitationAccepter() 4 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/aidog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { repeat } from './config.js' 8 | 9 | const getAidogRoomInviter = (_: WeChatSettings) => { 10 | const aidogConfig: RoomInviterConfig = { 11 | password: [ 12 | /^aidog$/i, 13 | ], 14 | repeat, 15 | room: /^Youth fed the 5th dog$/i, 16 | welcome: '禁止在本群测试机器人。 注意:老群已满,此群为AiDog第五个新群', 17 | } 18 | 19 | const AidogRoomInviter = RoomInviter(aidogConfig) 20 | return AidogRoomInviter 21 | } 22 | 23 | export { 24 | getAidogRoomInviter, 25 | } 26 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/bot5.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | const getBot5RoomInviter = (settings: WeChatSettings) => { 8 | const bot5Config: RoomInviterConfig = { 9 | password: [ 10 | /^bot5$/i, 11 | /^BotFriday$/i, 12 | /^Bot Friday$/i, 13 | /^friday$/i, 14 | ], 15 | room: settings.rooms.bot5Club.rooms[1], // <- [1] is the current year member room 16 | rule: [ 17 | "I'm trying to invite you into Bot5, please read the manual first: https://www.bot5.club/manuals/newcomer", 18 | ], 19 | welcome: [ 20 | 'Welcome to join us!', 21 | ], 22 | } 23 | 24 | const Bot5RoomInviter = RoomInviter(bot5Config) 25 | return Bot5RoomInviter 26 | } 27 | 28 | export { 29 | getBot5RoomInviter, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/config.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Contact, 3 | Room, 4 | } from 'wechaty' 5 | import type { 6 | talkers, 7 | } from 'wechaty-plugin-contrib' 8 | 9 | const repeat: talkers.ContactTalkerOptions = async (contact: Contact, room?: Room) => { 10 | await contact.say('You are already in our room: ' + await room?.topic()) 11 | } 12 | 13 | const WECHATY_DEVELOPERS_ROOM_RULES = [ 14 | 'Thanks for asking me to invite you for joining the "Wechaty Developers\' Home" WeChat Room!', 15 | 'Wechaty is a Conversational SDK for chatbot makers.', 16 | 'You can find our documentation at https://wechaty.js.org/docs/, and all archived discussions on https://gitter.im/wechaty/wechaty', 17 | 'Please introduce yourself after you join the room, cheers!', 18 | ] 19 | const WECHATY_DEVELOPERS_ROOM_WELCOME = [ 20 | ", welcome to join the Wechaty Python/Go/Java/PHP/.Net/Scala/Rust Developer's Home!", 21 | 'You can find Polyglot Wechaty docs at https://wechaty.js.org/docs/polyglot/ ,', 22 | 'Please go ahead to introduce yourself to the group.', 23 | ].join('\n') 24 | 25 | const WECHATY_DEVELOPERS_ROOM_RULES_CHINESE = [ 26 | '欢迎你加入 "Wechaty Developers\' Home" 微信群!', 27 | 'Wechaty 可以帮助你用 7 行代码接入聊天软件的实现自动化,', 28 | '官网 https://wechaty.js.org 可以查看新闻、博客和文档资料。', 29 | '请你在加入微信群后进行自我介绍,谢谢!', 30 | ] 31 | 32 | export { 33 | WECHATY_DEVELOPERS_ROOM_RULES, 34 | WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 35 | WECHATY_DEVELOPERS_ROOM_WELCOME, 36 | repeat, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/mod.ts: -------------------------------------------------------------------------------- 1 | export { getAidogRoomInviter } from './aidog.js' 2 | export { getBot5RoomInviter } from './bot5.js' 3 | export { getWechatyRoomInviter } from './wechaty.js' 4 | export { getWechatyChineseRoomInviter } from './wechaty-chinese.js' 5 | export { getWechatyEnglishRoomInviter } from './wechaty-english.js' 6 | export { getInviterPluginList } from './polyglot-wechaty.js' 7 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/wechaty-chinese.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 10 | } from './config.js' 11 | 12 | const getWechatyChineseRoomInviter = (settings: WeChatSettings) => { 13 | const wechatyChineseConfig: RoomInviterConfig = { 14 | password : [ 15 | /^wechaty chinese$/i, 16 | /^wechaty 中文$/i, 17 | /^中文$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.chinese, // DEVELOPERS_ROOM_ID_CHINESE, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 22 | welcome: [ 23 | ',欢迎你加入 Wechaty 中文开发者微信群!请发送一个简短的自我介绍向群友们做个介绍,谢谢!', 24 | ], 25 | } 26 | 27 | const WechatyChineseRoomInviter = RoomInviter(wechatyChineseConfig) 28 | return WechatyChineseRoomInviter 29 | } 30 | 31 | export { 32 | getWechatyChineseRoomInviter, 33 | } 34 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/wechaty-english.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES, 10 | WECHATY_DEVELOPERS_ROOM_WELCOME, 11 | } from './config.js' 12 | 13 | const getWechatyEnglishRoomInviter = (settings: WeChatSettings) => { 14 | const wechatyEnglishConfig: RoomInviterConfig = { 15 | password : [ 16 | /^wechaty english$/i, 17 | /^english$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.english, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES, 22 | welcome: WECHATY_DEVELOPERS_ROOM_WELCOME, 23 | } 24 | 25 | const WechatyEnglishRoomInviter = RoomInviter(wechatyEnglishConfig) 26 | return WechatyEnglishRoomInviter 27 | } 28 | 29 | export { 30 | getWechatyEnglishRoomInviter, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/room-inviters/wechaty.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES, 10 | WECHATY_DEVELOPERS_ROOM_WELCOME, 11 | } from './config.js' 12 | 13 | const getWechatyRoomInviter = (settings: WeChatSettings) => { 14 | const wechatyConfig: RoomInviterConfig = { 15 | password : [ 16 | /^wechaty$/i, 17 | /^plugin$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.home, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES, 22 | welcome: WECHATY_DEVELOPERS_ROOM_WELCOME, 23 | } 24 | 25 | const WechatyRoomInviter = RoomInviter(wechatyConfig) 26 | return WechatyRoomInviter 27 | } 28 | 29 | export { 30 | getWechatyRoomInviter, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/ai.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | 6 | // import { Chitchat } from '../../ml/chitchat.js' 7 | // import { Gpt } from '../../ml/gpt.js' 8 | import { Dreamily } from '../../../../../applications/ai-lib/dreamily.js' 9 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 10 | 11 | const getAiVorpalPlugin = (_settings: WeChatSettings) => { 12 | const aiConfig: WechatyVorpalConfig = { 13 | contact : true, 14 | mention : true, 15 | room : true, 16 | silent : true, 17 | 18 | use : [ 19 | // Chitchat(), 20 | // Gpt(), 21 | Dreamily(), 22 | ], 23 | } 24 | 25 | const AiVorpalPlugin = WechatyVorpal(aiConfig) 26 | return AiVorpalPlugin 27 | } 28 | 29 | export { 30 | getAiVorpalPlugin, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/chatops-ddr.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ddr, 7 | } from 'wechaty-vorpal-contrib' 8 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getDdrVorpalPlugin = (settings: WeChatSettings) => { 11 | const donutConfig: WechatyVorpalConfig = { 12 | contact : false, 13 | mention : false, 14 | room : [ 15 | settings.rooms.chatops.ddr, 16 | ], 17 | silent : true, 18 | 19 | use : [ 20 | Ddr(), 21 | ], 22 | } 23 | const DdrVorpalPlugin = WechatyVorpal(donutConfig) 24 | return DdrVorpalPlugin 25 | } 26 | 27 | export { 28 | getDdrVorpalPlugin, 29 | } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/chatops-friday.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ding, 7 | Eval, 8 | Cash, 9 | UrlLink, 10 | Announce, 11 | Find, 12 | MathMaster, 13 | Version, 14 | Whoru, 15 | } from 'wechaty-vorpal-contrib' 16 | 17 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 18 | 19 | const getChatopsVorpalPlugin = (settings: WeChatSettings) => { 20 | const chatopsConfig: WechatyVorpalConfig = { 21 | contact : false, 22 | mention : false, 23 | room : settings.rooms.chatops.friday, 24 | silent : true, 25 | 26 | use: [ 27 | Ding(), 28 | Eval(), 29 | Cash(), 30 | UrlLink(), 31 | Announce(), 32 | Find(), 33 | MathMaster(), 34 | Whoru(), 35 | Version(), 36 | ], 37 | } 38 | 39 | const ChatopsVorpalPlugin = WechatyVorpal(chatopsConfig) 40 | return ChatopsVorpalPlugin 41 | } 42 | 43 | export { 44 | getChatopsVorpalPlugin, 45 | } 46 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/chatops-preangel.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | UrlLink, 7 | } from 'wechaty-vorpal-contrib' 8 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getPreAngelVorpalPlugin = (settings: WeChatSettings) => { 11 | const config: WechatyVorpalConfig = { 12 | contact : false, 13 | mention : false, 14 | room : settings.rooms.chatops.preangel, 15 | silent : true, 16 | 17 | use: [ 18 | UrlLink(), 19 | ], 20 | } 21 | const PreAngelVorpalPlugin = WechatyVorpal(config) 22 | return PreAngelVorpalPlugin 23 | } 24 | 25 | export { 26 | getPreAngelVorpalPlugin, 27 | } 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/contributors.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | UrlLink, 7 | Find, 8 | MathMaster, 9 | Version, 10 | Whoru, 11 | } from 'wechaty-vorpal-contrib' 12 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 13 | 14 | const getContributorsVorpalPlugin = (settings: WeChatSettings) => { 15 | const contributorsConfig: WechatyVorpalConfig = { 16 | contact : false, 17 | mention : true, 18 | room : settings.rooms.wechatyDevelopers.contributors, // CONTRIBUTORS_ROOM_ID, 19 | silent : true, 20 | 21 | use : [ 22 | UrlLink(), 23 | Find(), 24 | MathMaster(), 25 | Whoru(), 26 | Version(), 27 | ], 28 | } 29 | 30 | const ContributorsVorpalPlugin = WechatyVorpal(contributorsConfig) 31 | return ContributorsVorpalPlugin 32 | } 33 | 34 | export { 35 | getContributorsVorpalPlugin, 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/direct-message.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | MathMaster, 7 | Version, 8 | Whoru, 9 | } from 'wechaty-vorpal-contrib' 10 | 11 | import hackerNews from 'vorpal-hacker-news' 12 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 13 | 14 | const dmConfig: WechatyVorpalConfig = { 15 | contact : true, 16 | room : false, 17 | silent : true, 18 | 19 | use: [ 20 | MathMaster(), 21 | Whoru(), 22 | Version(), 23 | /** 24 | * https://github.com/vorpaljs/vorpal-hacker-news 25 | * hacker-news --length 3 26 | */ 27 | hackerNews, 28 | ], 29 | } 30 | 31 | const getDirectMessageVorpalPlugin = (_settings: WeChatSettings) => { 32 | const DirectMessageVorpalPlugin = WechatyVorpal(dmConfig) 33 | return DirectMessageVorpalPlugin 34 | } 35 | 36 | export { 37 | getDirectMessageVorpalPlugin, 38 | } 39 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | import * as contributors from './contributors.js' 2 | import * as dm from './direct-message.js' 3 | import * as friday from './chatops-friday.js' 4 | import * as preangel from './chatops-preangel.js' 5 | import * as ddr from './chatops-ddr.js' 6 | import * as faq from './qnamaker.js' 7 | import * as ai from './ai.js' 8 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getVorpalPlugins = (settings: WeChatSettings) => { 11 | const vorpalPluginList = [ 12 | ...Object.values(friday).map(get => get(settings)), 13 | ...Object.values(preangel).map(get => get(settings)), 14 | ...Object.values(contributors).map(get => get(settings)), 15 | ...Object.values(dm).map(get => get(settings)), 16 | ...Object.values(ddr).map(get => get(settings)), 17 | ...Object.values(faq).map(get => get(settings)), 18 | ...Object.values(ai).map(get => get(settings)), 19 | ] 20 | 21 | return vorpalPluginList 22 | } 23 | 24 | export { getVorpalPlugins } 25 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/plugins/vorpals/qnamaker.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import type { WeChatSettings } from '../../../../../wechaty-settings/mod.js' 6 | // import { Faq } from 'wechaty-qnamaker' 7 | 8 | // import { 9 | // configChinese, 10 | // configEnglish, 11 | // } from '../qnamaker.js' 12 | 13 | const getFaqVorpalPlugin = (settings: WeChatSettings) => { 14 | const faqConfig: WechatyVorpalConfig = { 15 | contact : false, 16 | mention : false, 17 | room : [ 18 | settings.rooms.chatops.friday, 19 | ...settings.rooms.wechatyDevelopers.contributors, // CONTRIBUTORS_ROOM_ID, 20 | ], 21 | silent : true, 22 | 23 | use: [ 24 | // Faq([ 25 | // configChinese, 26 | // configEnglish, 27 | // ]), 28 | ], 29 | } 30 | 31 | const FaqVorpalPlugin = WechatyVorpal(faqConfig) 32 | return FaqVorpalPlugin 33 | } 34 | 35 | export { 36 | getFaqVorpalPlugin, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/wechat/set-handlers.ts: -------------------------------------------------------------------------------- 1 | import type { Wechaty } from 'wechaty' 2 | import type { WeChatSettings } from '../../../wechaty-settings/mod.js' 3 | 4 | import onError from './handlers/on-error.js' 5 | import { getOnFriendship } from './handlers/on-friendship.js' 6 | import onLogin from './handlers/on-login.js' 7 | import onLogout from './handlers/on-logout.js' 8 | import onMessage from './handlers/on-message.js' 9 | import onRoomInvite from './handlers/on-room-invite.js' 10 | import onRoomJoin from './handlers/on-room-join.js' 11 | import onRoomLeave from './handlers/on-room-leave.js' 12 | import onRoomTopic from './handlers/on-room-topic.js' 13 | import onScan from './handlers/on-scan.js' 14 | 15 | const setHandlers = (wechaty: Wechaty, settings: WeChatSettings) => { 16 | wechaty.on('error', onError) 17 | wechaty.on('friendship', getOnFriendship(settings)) 18 | wechaty.on('login', onLogin) 19 | wechaty.on('logout', onLogout) 20 | wechaty.on('message', onMessage) 21 | wechaty.on('room-invite', onRoomInvite) 22 | wechaty.on('room-join', onRoomJoin) 23 | wechaty.on('room-leave', onRoomLeave) 24 | wechaty.on('room-topic', onRoomTopic) 25 | wechaty.on('scan', onScan) 26 | } 27 | 28 | export { 29 | setHandlers, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/whatsapp/plugins/mod.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Wechaty Plugin Support with KickOut Example #1939 3 | * https://github.com/wechaty/wechaty/issues/1939 4 | */ 5 | import { 6 | DingDong, 7 | EventLogger, 8 | } from 'wechaty-plugin-contrib' 9 | 10 | // import { 11 | // QnAMakerCeibsPlugin, 12 | // } from './qnamaker.js' 13 | 14 | import { vorpalPluginList } from './vorpals/mod.js' 15 | 16 | const getPlugins = () => { 17 | 18 | const pluginList = [ 19 | DingDong(), 20 | EventLogger(), 21 | // QnAMakerCeibsPlugin, 22 | ] 23 | return [ 24 | ...pluginList, 25 | ...vorpalPluginList, 26 | ] 27 | } 28 | 29 | export { getPlugins } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/whatsapp/plugins/vorpals/contrib.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ding, 7 | MathMaster, 8 | Version, 9 | Whoru, 10 | } from 'wechaty-vorpal-contrib' 11 | 12 | const chatopsConfig: WechatyVorpalConfig = { 13 | contact : true, 14 | silent : true, 15 | 16 | use: [ 17 | Ding(), 18 | MathMaster(), 19 | Whoru(), 20 | Version(), 21 | ], 22 | } 23 | 24 | const ChatopsVorpalPlugin = WechatyVorpal(chatopsConfig) 25 | 26 | export { 27 | ChatopsVorpalPlugin, 28 | } 29 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/whatsapp/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | import * as contrib from './contrib.js' 2 | 3 | const vorpalPluginList = [ 4 | ...Object.values(contrib), 5 | ] 6 | 7 | export { vorpalPluginList } 8 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/whatsapp/whatsapp-builder.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import * as WECHATY from 'wechaty' 3 | import { Brolog } from 'brolog' 4 | import { PuppetWhatsapp } from 'wechaty-puppet-whatsapp' 5 | 6 | import { WhatsAppSettings } from '../../../wechaty-settings/mod.js' 7 | import { getPlugins } from './plugins/mod.js' 8 | import type { Builder } from '../builder.js' 9 | 10 | @Injectable() 11 | class WhatsAppBuilder implements Builder { 12 | 13 | constructor ( 14 | private readonly log: Brolog, 15 | public readonly settings: WhatsAppSettings, 16 | ) { 17 | this.log.verbose('WhatsAppBuilder', 'constructor(%s)', 18 | settings.name, 19 | ) 20 | } 21 | 22 | build () { 23 | this.log.verbose('WhatsAppBuilder', 'build()') 24 | 25 | const puppet = new PuppetWhatsapp() 26 | 27 | const wechaty = WECHATY.WechatyBuilder.build({ 28 | name: this.settings.name, 29 | puppet, 30 | }) 31 | 32 | wechaty.use(getPlugins()) 33 | 34 | return wechaty 35 | } 36 | 37 | } 38 | 39 | export { WhatsAppBuilder } 40 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/get-io-client.ts: -------------------------------------------------------------------------------- 1 | // import { 2 | // Wechaty, 3 | // log, 4 | // IoClient, 5 | // } from 'wechaty' 6 | // import type { 7 | // IoClientOptions, 8 | // } from 'wechaty/dist/esm/src/io-client.js' 9 | 10 | // import type { WorkProSettings } from '../../../wechaty-settings/mod' 11 | 12 | // function getIoClient ( 13 | // wechaty: Wechaty, 14 | // settings: WorkProSettings, 15 | // ) { 16 | // log.verbose('getWechaty', 'getIoClient(%s)', wechaty) 17 | 18 | // const token = settings.wechatyToken 19 | // const port = settings.wechatyPuppetServerPort 20 | 21 | // if (!port) { 22 | // throw new Error('port not found: please set WECHATY_PUPPET_SERVER_PORT in environment before start') 23 | // } 24 | 25 | // const options: IoClientOptions = { 26 | // port, 27 | // token, 28 | // wechaty, 29 | // } 30 | 31 | // const client = new IoClient(options) 32 | 33 | // return client 34 | // } 35 | 36 | // export { getIoClient } 37 | export {} 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/get-memory.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | } from 'wechaty' 4 | import { 5 | MemoryCard, 6 | } from 'memory-card' 7 | import type { EnvVar } from '../../../infrastructures/mod.js' 8 | 9 | export let memory: undefined | MemoryCard 10 | 11 | export function getMemory ( 12 | name: string, 13 | envVar: EnvVar, 14 | ): MemoryCard { 15 | log.verbose('getMemory', 'getMemory(%s)', name) 16 | 17 | if (memory) { 18 | return memory 19 | } 20 | 21 | const AWS_ACCESS_KEY_ID = envVar.get('AWS_ACCESS_KEY_ID').asString() 22 | const AWS_SECRET_ACCESS_KEY = envVar.get('AWS_SECRET_ACCESS_KEY').asString() 23 | const AWS_REGION = envVar.get('AWS_REGION').asString() 24 | const AWS_S3_BUCKET = envVar.get('AWS_S3_BUCKET').asString() 25 | 26 | if (AWS_ACCESS_KEY_ID 27 | && AWS_REGION 28 | && AWS_SECRET_ACCESS_KEY 29 | && AWS_S3_BUCKET 30 | ) { 31 | log.verbose('getMemory', 'getMemory() creating new s3 memory') 32 | 33 | memory = new MemoryCard({ 34 | name, 35 | storageOptions: { 36 | accessKeyId : AWS_ACCESS_KEY_ID, 37 | bucket : AWS_S3_BUCKET, 38 | region : AWS_REGION, 39 | secretAccessKey : AWS_SECRET_ACCESS_KEY, 40 | type : 's3', 41 | }, 42 | }) 43 | } else { 44 | log.verbose('getMemory', 'getMemory() creating new file memory') 45 | memory = new MemoryCard({ name }) 46 | } 47 | 48 | return memory 49 | } 50 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-error.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Wechaty, 4 | } from 'wechaty' 5 | 6 | export default async function onError ( 7 | this : Wechaty, 8 | e : Error, 9 | ): Promise { 10 | log.error('on-error', 'onError(%s)', e) 11 | console.error(e) 12 | console.error(e.stack) 13 | } 14 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-friendship.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Friendship, 4 | Wechaty, 5 | } from 'wechaty' 6 | import type { WorkProSettings } from '../../../../wechaty-settings/mod.js' 7 | 8 | const getOnFriendship = (settings: WorkProSettings) => { 9 | return async function onFriendship ( 10 | this : Wechaty, 11 | friendship : Friendship, 12 | ): Promise { 13 | log.info('on-friendship', 'onFriendship(%s)', friendship) 14 | 15 | const contact = await friendship.contact() 16 | const hello = await friendship.hello() 17 | 18 | let text: string 19 | if (friendship.type() === this.Friendship.Type.Receive) { 20 | text = `received friendship from ${contact} with ${hello}` 21 | } else if (friendship.type() === this.Friendship.Type.Confirm) { 22 | text = `confirmed friendship from ${contact}` 23 | } else { 24 | throw new Error('unknown friendship type: ' + friendship.type()) 25 | } 26 | 27 | const room = await this.Room.find({ id: settings.rooms.chatops.friday }) 28 | if (!room) { 29 | throw new Error('room id: ' + settings.rooms.chatops.friday + ' not found') 30 | } 31 | await room.say(text) 32 | } 33 | } 34 | 35 | export { 36 | getOnFriendship, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-login.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | VERSION, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onLogin ( 9 | this : Wechaty, 10 | user : Contact, 11 | ): Promise { 12 | const msg = `${user.name()} BOT5 Wechaty@${VERSION} logged in` 13 | log.info('startBot', 'onLogin(%s) %s', user, msg) 14 | await user.say(msg) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-logout.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Wechaty, 5 | } from 'wechaty' 6 | 7 | export default async function onLogout ( 8 | this : Wechaty, 9 | user : Contact, 10 | ): Promise { 11 | log.info('on-logout', 'onLogout(%s)', user) 12 | } 13 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-room-invite.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | RoomInvitation, 4 | Wechaty, 5 | } from 'wechaty' 6 | 7 | export default async function onRoomInvite ( 8 | this : Wechaty, 9 | roomInvitation : RoomInvitation, 10 | ): Promise { 11 | log.info('on-room-invite', 'onRoomInvite(%s)', roomInvitation) 12 | } 13 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-room-join.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomJoin ( 9 | this : Wechaty, 10 | room : Room, 11 | inviteeList : Contact[], 12 | inviter : Contact, 13 | ): Promise { 14 | log.info('on-room-join', 'onRoomJoin(%s, %s, %s)', room, inviteeList.join(','), inviter) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-room-leave.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomLeave ( 9 | this : Wechaty, 10 | room : Room, 11 | leaverList : Contact[], 12 | remover? : Contact, 13 | ): Promise { 14 | log.info('on-room-leave', 'onRoomLeave(%s, %s, %s)', room, leaverList.join(','), remover) 15 | } 16 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-room-topic.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contact, 3 | log, 4 | Room, 5 | Wechaty, 6 | } from 'wechaty' 7 | 8 | export default async function onRoomTopic ( 9 | this : Wechaty, 10 | room : Room, 11 | newTopic : string, 12 | oldTopic : string, 13 | changer : Contact, 14 | ): Promise { 15 | log.info('startBot', 'onRoomTopic(%s, %s, %s, %s)', room, newTopic, oldTopic, changer) 16 | } 17 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/handlers/on-scan.ts: -------------------------------------------------------------------------------- 1 | import { 2 | log, 3 | Wechaty, 4 | } from 'wechaty' 5 | import { 6 | ScanStatus, 7 | } from 'wechaty-puppet/types' 8 | 9 | export default async function onScan ( 10 | this : Wechaty, 11 | qrcode : string, 12 | status : number, 13 | ): Promise { 14 | log.info('on-scan', 'onScan() [%s(%s)] %s', 15 | ScanStatus[status], 16 | status, 17 | qrcode, 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/bot5-assistant.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Bot5Assistant, 3 | } from 'wechaty-bot5-assistant' 4 | import type { WorkProSettings } from '../../../../wechaty-settings/mod.js' 5 | 6 | const getBot5AssistantPlugin = (settings: WorkProSettings) => { 7 | const [ _next, current, ..._previous ] = settings.rooms.bot5Club.rooms 8 | 9 | const Bot5AssistantPlugin = Bot5Assistant({ 10 | room: [ 11 | current, 12 | /^BOT5 Assistant/i, // room topic 13 | ], 14 | }) 15 | return Bot5AssistantPlugin 16 | } 17 | 18 | export { 19 | getBot5AssistantPlugin, 20 | } 21 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/chatops.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChatOps, 3 | } from 'wechaty-plugin-contrib' 4 | import type { WorkProSettings } from '../../../../wechaty-settings/mod.js' 5 | 6 | const getChatOpsPlugin = (settings: WorkProSettings) => { 7 | const ChatOpsPlugin = ChatOps({ 8 | blacklist: [ 9 | 'bot-sentry', 10 | ], 11 | room: settings.rooms.chatops.friday, 12 | }) 13 | return ChatOpsPlugin 14 | } 15 | 16 | export { 17 | getChatOpsPlugin, 18 | } 19 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/crontab.ts: -------------------------------------------------------------------------------- 1 | 2 | import cron from 'node-cron' 3 | 4 | interface CRONConfig { 5 | time: string, 6 | reply: string 7 | } 8 | 9 | const CRON_CONFIG: CRONConfig[] = [ 10 | { 11 | reply: '星期一了,如果主席还没发活动总结的话要注意了', 12 | /** 13 | * 定时任务 14 | * ┌─────────────── second (optional) 15 | * │ ┌───────────── minute 16 | * │ │ ┌─────────── hour 17 | * │ │ │ ┌──────── day of month 18 | * │ │ │ │ ┌────── month 19 | * │ │ │ │ │ ┌──── day of week 20 | * │ │ │ │ │ │ 21 | * │ │ │ │ │ │ 22 | * * * * * * * // */ 23 | time: '0 0 19 * * 1', 24 | }, 25 | ] 26 | 27 | /** 28 | * TODO: Huan(202006) 29 | */ 30 | export async function crontab () { 31 | for (const cronConfig of CRON_CONFIG) { 32 | cron.schedule(cronConfig.time, () => { 33 | // await Chatops.instance().say(cronConfig.reply) 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/ding-dong/mod.ts: -------------------------------------------------------------------------------- 1 | import { DingDong } from 'wechaty-plugin-contrib' 2 | 3 | const DEFAULT_CONFIG = { 4 | contact : true, 5 | room : false, 6 | } 7 | 8 | const wechatyDingDongConfig = { 9 | ding: [ 10 | /^wechaty$/i, 11 | /^(\w+)*\s*wechaty\s*(\w+)*$/i, 12 | ], 13 | dong: [ 14 | "Welcome to join our Wechaty Developers' Home by visiting https://gitter.im/wechaty/wechaty", 15 | 'Currently, the WeChat room invitation is temporary not available because we are experiencing issue #62, learn more from https://github.com/wechaty/friday/issues/62 if you are interested.', 16 | 'If you want to join the WeChat room, please come back and send me "Wechaty" again after December 3rd.', 17 | ], 18 | } 19 | 20 | const WechatyDingDongPlugin = DingDong({ 21 | ...DEFAULT_CONFIG, 22 | ...wechatyDingDongConfig, 23 | }) 24 | 25 | export { 26 | WechatyDingDongPlugin, 27 | } 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/freshdesk.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyFreshdesk, 4 | // WechatyFreshdeskConfig, 5 | // } from 'wechaty-freshdesk' 6 | 7 | // const config: WechatyFreshdeskConfig = { 8 | // close: [ 9 | // [ 10 | // 'Thank you for contacting the support of puppet service provider.', 11 | // 'We believe that your question has been answered and hope you are good now.', 12 | // 'Please feel free to contact us again if you have more questions.', 13 | // 'Thank you very much, and have a nice day!', 14 | // ].join(' '), 15 | // ], 16 | // contact: true, 17 | // room: false, 18 | 19 | // /** 20 | // * Huan(202201) 21 | // * TODO: use FridayConfig for the environment variables 22 | // */ 23 | // apiKey : process.env['WECHATY_PLUGIN_FRESHDESK_API_KEY'], 24 | // portalUrl : process.env['WECHATY_PLUGIN_FRESHDESK_PORTAL_URL'], 25 | // webhookProxyUrl : process.env['WECHATY_PLUGIN_FRESHDESK_WEBHOOK_PROXY_URL'], 26 | // } 27 | 28 | // export const FreshdeskPlugin = WechatyFreshdesk(config) 29 | export {} 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/friendship-accepter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FriendshipAccepter, 3 | FriendshipAccepterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | 6 | const NEW_FRIEND_GREETING = [ 7 | "Hi there, my name is Friday, I'm a Chatbot built by Wechaty and for Wechaty.", 8 | "I'm open-sourced, please feel free to visit https://github.com/wechaty/friday if you want to know me more.", 9 | "Currently, I'm in charge of managing some developers' WeChat group: if you know any secret words, please send to me, then I'll be able to invite you to join!", 10 | ] 11 | 12 | const config: FriendshipAccepterConfig = { 13 | greeting: NEW_FRIEND_GREETING, 14 | // keyword: '42', 15 | } 16 | 17 | export const FriendshipAccepterPlugin = FriendshipAccepter(config) 18 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/heartbeat.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Heartbeat, 3 | HeartbeatConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../wechaty-settings/mod.js' 6 | 7 | const getHeartbeatPlugin = (settings: WorkProSettings) => { 8 | const config: HeartbeatConfig = { 9 | emoji: { 10 | heartbeat : '[爱心]', 11 | login : '[太阳]', 12 | logout : '[月亮]', 13 | ready : '[拳头]', 14 | }, 15 | intervalSeconds: 60 * 60, // 1 hour 16 | room: settings.rooms.chatops.heartbeat, 17 | } 18 | 19 | const HeartbeatPlugin = Heartbeat(config) 20 | return HeartbeatPlugin 21 | } 22 | 23 | export { 24 | getHeartbeatPlugin, 25 | } 26 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/intercom.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyIntercom, 4 | // WechatyIntercomConfig, 5 | // } from 'wechaty-intercom' 6 | 7 | // const config: WechatyIntercomConfig = { 8 | // mention: true, 9 | // close: [ 10 | // [ 11 | // 'Thank you for contacting the support of puppet service provider.', 12 | // 'We believe that your question has been answered and hope you are good now.', 13 | // 'Please feel free to contact us again if you have more questions.', 14 | // 'Thank you very much, and have a nice day!', 15 | // ].join(' '), 16 | // ], 17 | // room: false, 18 | 19 | // /** 20 | // * Huan(202201): TODO: use FridayConfig for the environment variables 21 | // */ 22 | // intercomToken : process.env['WECHATY_PLUGIN_INTERCOM_TOKEN'], 23 | // webhookProxyUrl : process.env['WECHATY_PLUGIN_INTERCOM_WEBHOOK_PROXY_URL'], 24 | // } 25 | 26 | // export const IntercomPlugin = WechatyIntercom(config) 27 | export {} 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/qnamaker.ts: -------------------------------------------------------------------------------- 1 | // /* eslint-disable sort-keys */ 2 | // import { 3 | // WechatyQnAMaker, 4 | // WechatyQnAMakerConfig, 5 | // } from 'wechaty-qnamaker' 6 | 7 | // import { botSettings } from '../../../wechaty-settings/deprecated.js' 8 | 9 | // const skipMessage = [ 10 | // /^wechaty$/i, 11 | // ] 12 | 13 | // const room = [ 14 | // ...botSettings.weChat.rooms.wechatyDevelopers.home, // DEVELOPERS_ROOM_ID_LIST, 15 | // ...Object.values(botSettings.weChat.rooms.wechatyUserGroup).flat(), // MULTI_LANG_ROOM_ID, 16 | // ] 17 | 18 | // const scoreThreshold = 30 19 | 20 | // const options = { 21 | // contact: true, 22 | // mention: true, 23 | // room, 24 | // scoreThreshold, 25 | // skipMessage, 26 | // } 27 | 28 | // /** 29 | // * Huan(202201): TODO: use `FridayConfig` for the environment variables 30 | // */ 31 | // export const configEnglish: WechatyQnAMakerConfig = { 32 | // ...options, 33 | // language : ['english'], 34 | // endpointKey : process.env['WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY'], 35 | // knowledgeBaseId : process.env['WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID'], 36 | // resourceName : process.env['WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME'], 37 | // } 38 | 39 | // export const configChinese: WechatyQnAMakerConfig = { 40 | // ...options, 41 | // language : ['chinese'], 42 | // endpointKey : process.env['WECHATY_PLUGIN_QNAMAKER_ENDPOINT_KEY_CHINESE'], 43 | // knowledgeBaseId : process.env['WECHATY_PLUGIN_QNAMAKER_KNOWLEDGE_BASE_ID_CHINESE'], 44 | // resourceName : process.env['WECHATY_PLUGIN_QNAMAKER_RESOURCE_NAME_CHINESE'], 45 | // } 46 | 47 | // const QnAMakerEnglishPlugin = WechatyQnAMaker(configEnglish) 48 | // const QnAMakerChinesePlugin = WechatyQnAMaker(configChinese) 49 | 50 | // export { 51 | // QnAMakerChinesePlugin, 52 | // QnAMakerEnglishPlugin, 53 | // } 54 | export {} 55 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/abbr-room-topic-by-regex.spec.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S node --no-warnings --loader ts-node/esm 2 | /** 3 | * Wechaty - https://github.com/chatie/wechaty 4 | * 5 | * @copyright 2016-2018 Huan LI 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * 19 | */ 20 | 21 | import { test } from 'tstest' 22 | import type { Message } from 'wechaty' 23 | 24 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 25 | 26 | test('abbrRoomTopicForAll()', async t => { 27 | const FIXTURES = [ 28 | [ "Wechaty Developers' Home 8", 'Home 8' ], 29 | [ "Wechaty Developers' Home", 'Home' ], 30 | [ 'Python Wechaty User Group', 'Python' ], 31 | [ 'Wechaty Broadcast Station', 'Station' ], 32 | [ 'BOT5 Club Open Forum - BFOF 2021', 'BOT5' ], 33 | [ 'BOT5 Club Alumni - 2022', 'BOT5' ], 34 | [ 'Wechaty Puppet WhatsApp', 'WhatsApp' ], 35 | ] 36 | 37 | for (const [ topic, expected ] of FIXTURES as [string, string][]) { 38 | const actual = await abbrRoomTopicForAll({ 39 | room: () => ({ 40 | topic: () => topic, 41 | }), 42 | } as any as Message) 43 | t.equal(actual, expected, `should convert "${topic}" => "${expected}"`) 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/abbr-room-topic-by-regex.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from 'wechaty' 2 | 3 | function abbrRoomTopicByRegex (matcher: RegExp) { 4 | return async function abbrRoomTopic (message: Message): Promise { 5 | const room = message.room() 6 | if (!room) { 7 | return 8 | } 9 | 10 | const topic = await room.topic() 11 | const matched = topic.match(matcher) 12 | 13 | if (!matched) { 14 | return 15 | } 16 | 17 | return matched[1] 18 | } 19 | } 20 | 21 | /** 22 | * "Wechaty Developers' Home 8" -> "Home 8" 23 | */ 24 | const abbrRoomTopicForDevelopersHome = abbrRoomTopicByRegex(/^Wechaty\s+.*?([^\s]+\s*(\d+)?)$/i) 25 | /** 26 | * "Python Wechaty User Group" -> "Python" 27 | */ 28 | const abbrRoomTopicForPolyglot = abbrRoomTopicByRegex(/^\s*([^\s]+)\s+Wechaty User Group$/i) 29 | 30 | /** 31 | * "Wechaty Puppet WhatsApp" -> "WhatsApp" 32 | */ 33 | const abbrRoomTopicForPuppet = abbrRoomTopicByRegex(/^Wechaty Puppet ([^\s]+)$/i) 34 | 35 | const abbrRoomTopicForBot5 = abbrRoomTopicByRegex(/^(BOT5) /) 36 | 37 | const abbrRoomTopicForAll = async (message: Message) => { 38 | return await abbrRoomTopicForDevelopersHome(message) 39 | || await abbrRoomTopicForPolyglot(message) 40 | || await abbrRoomTopicForPuppet(message) 41 | || await abbrRoomTopicForBot5(message) 42 | } 43 | 44 | export { 45 | abbrRoomTopicByRegex, 46 | abbrRoomTopicForDevelopersHome, 47 | abbrRoomTopicForAll, 48 | } 49 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/bidirectional-mapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | // import { 6 | // mappers, 7 | // } from 'wechaty-plugin-contrib' 8 | 9 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 10 | import { senderDisplayName } from './sender-display-name.js' 11 | 12 | const bidirectionalMapper = async (message: Message) => { 13 | // Drop all messages if not Text 14 | if (message.type() !== types.Message.Text) { return } 15 | 16 | const talkerDisplayName = await senderDisplayName(message) 17 | const roomShortName = await abbrRoomTopicForAll(message) || 'Nowhere' 18 | 19 | const text = message.text() 20 | 21 | return `[${talkerDisplayName}@${roomShortName}]: ${text}` 22 | } 23 | 24 | export { bidirectionalMapper } 25 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/bot5-club/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WorkProSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * BOT5 Club 12 | * 13 | */ 14 | const getBot5OneToManyPlugin = (settings: WorkProSettings) => { 15 | /** 16 | * - first club room in the array is the latest room (current year) 17 | * - second club room in the array (and all the following) 18 | * is the previous room (previous year). 19 | */ 20 | const [ next, current, ...previous ] = settings.rooms.bot5Club.rooms 21 | 22 | const Bot5OneToManyPlugin = SourceToTargetRoomConnector({ 23 | map: getUnidirectionalMapper(settings), 24 | source: [ 25 | next, 26 | current, 27 | ], 28 | target: [ 29 | ...previous, 30 | ], 31 | }) 32 | return Bot5OneToManyPlugin 33 | } 34 | 35 | export { 36 | getBot5OneToManyPlugin, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './bot5-club/mod.js' 2 | export * from './wechaty-developers-home/mod.js' 3 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/sender-display-name.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from 'wechaty' 2 | import { log } from 'wechaty-puppet' 3 | 4 | const senderDisplayName = async (message: Message) => { 5 | const room = message.room() 6 | 7 | let displayName 8 | try { 9 | const talker = message.talker() 10 | const alias = await room?.alias(talker) 11 | displayName = alias || talker.name() || 'Noname' 12 | } catch (e) { 13 | log.error('senderDisplayName', 'message.talker() exception: %s', (e as Error).message) 14 | displayName = 'unknown' 15 | } 16 | 17 | return displayName 18 | } 19 | 20 | export { senderDisplayName } 21 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/unidirectional-mapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { abbrRoomTopicForAll } from './abbr-room-topic-by-regex.js' 8 | import { senderDisplayName } from './sender-display-name.js' 9 | 10 | const getUnidirectionalMapper = (settings: WorkProSettings) => { 11 | const skipRoomList = [ 12 | ...settings.rooms.wechatyDevelopers.homeHq, 13 | ...settings.rooms.wechatyDevelopers.broadcastStation, 14 | ] 15 | /** 16 | * 17 | * Message Mapper for Room Connectors 18 | * 19 | */ 20 | const unidirectionalMapper = async (message: Message) => { 21 | const talkerDisplayName = await senderDisplayName(message) 22 | const roomShortName = await abbrRoomTopicForAll(message) || 'Nowhere' 23 | 24 | const prefix = `[${talkerDisplayName}@${roomShortName}]` 25 | 26 | const messageList: (string | Message)[] = [] 27 | 28 | switch (message.type()) { 29 | case types.Message.Text: 30 | messageList.push(`${prefix}: ${message.text()}`) 31 | break 32 | 33 | default: // Forward all non-Text messages 34 | messageList.push(message) 35 | { 36 | const room = message.room() 37 | /** 38 | * If message is not sent from Headquarters Room, 39 | * then we add a sender information for the destination rooms. 40 | */ 41 | if (room && !skipRoomList.includes(room.id)) { 42 | const msgType = types.Message[message.type()] 43 | messageList.unshift(`${prefix}: ${msgType}`) 44 | } 45 | } 46 | break 47 | } 48 | 49 | return messageList 50 | } 51 | return unidirectionalMapper 52 | } 53 | 54 | export { getUnidirectionalMapper } 55 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/broadcast-station.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WorkProSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * Announcement 12 | * 13 | */ 14 | const getHeadquartersBroadcastStationPlugin = (settings: WorkProSettings) => { 15 | const exceptBroadcastStation = (roomId: string) => !settings.rooms.wechatyDevelopers.broadcastStation.includes(roomId) 16 | 17 | const allUserGroups = [ 18 | ...Object.values(settings.rooms.polyglotUserGroup).flat(), 19 | ...Object.values(settings.rooms.puppetUserGroup).flat(), 20 | ] 21 | const allHomes = Object.values(settings.rooms.wechatyDevelopers).flat() 22 | .filter(exceptBroadcastStation) 23 | 24 | const HeadquartersBroadcastStationPlugin = SourceToTargetRoomConnector({ 25 | map: getUnidirectionalMapper(settings), 26 | source: [ 27 | ...settings.rooms.wechatyDevelopers.broadcastStation, 28 | ], 29 | target: [ 30 | ...allUserGroups, 31 | ...allHomes, 32 | ], 33 | }) 34 | return HeadquartersBroadcastStationPlugin 35 | } 36 | 37 | export { 38 | getHeadquartersBroadcastStationPlugin, 39 | } 40 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/home-hq.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WorkProSettings } from '../../../../../../wechaty-settings/mod.js' 6 | 7 | import { getUnidirectionalMapper } from '../unidirectional-mapper.js' 8 | 9 | /** 10 | * 11 | * Collecting all messages from developers' home X 12 | * 13 | */ 14 | const getHomeHqCollectingPlugin = (settings: WorkProSettings) => { 15 | const HomeHqCollectingPlugin = SourceToTargetRoomConnector({ 16 | blacklist: [ 17 | settings.mikeId, 18 | ], 19 | map: getUnidirectionalMapper(settings), 20 | source: [ 21 | ...settings.rooms.wechatyDevelopers.home, 22 | ], 23 | target: [ 24 | ...settings.rooms.wechatyDevelopers.homeHq, 25 | ], 26 | }) 27 | return HomeHqCollectingPlugin 28 | } 29 | 30 | /** 31 | * 32 | * Broadcasting text and url link from home HQ to developers' home X 33 | * 34 | */ 35 | const getHomeHqAnnouncingPlugin = (settings: WorkProSettings) => { 36 | const HomeHqAnnouncingPlugin = SourceToTargetRoomConnector({ 37 | blacklist: [ 38 | settings.mikeId, 39 | ], 40 | map: getUnidirectionalMapper(settings), 41 | source: [ 42 | ...settings.rooms.wechatyDevelopers.homeHq, 43 | ], 44 | target: [ 45 | ...settings.rooms.wechatyDevelopers.home, 46 | ...settings.rooms.wechatyDevelopers.monitor, 47 | ], 48 | }) 49 | return HomeHqAnnouncingPlugin 50 | } 51 | 52 | export { 53 | getHomeHqAnnouncingPlugin, 54 | getHomeHqCollectingPlugin, 55 | } 56 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/home-mixer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | types, 4 | } from 'wechaty' 5 | import { 6 | ManyToManyRoomConnector, 7 | } from 'wechaty-plugin-contrib' 8 | 9 | import type { WorkProSettings } from '../../../../../../wechaty-settings/mod.js' 10 | 11 | import { bidirectionalMapper } from '../bidirectional-mapper.js' 12 | 13 | /** 14 | * 15 | * Many to Many 16 | * 17 | */ 18 | const getManyToManyPlugin = (settings: WorkProSettings) => { 19 | const blacklist = [ 20 | async (message: Message) => message.type() !== types.Message.Text, 21 | settings.mikeId, 22 | ] 23 | 24 | const ManyToManyPlugin = ManyToManyRoomConnector({ 25 | blacklist, 26 | many: [ 27 | ...settings.rooms.wechatyDevelopers.home, 28 | ], 29 | map: bidirectionalMapper, 30 | }) 31 | return ManyToManyPlugin 32 | } 33 | 34 | export { 35 | getManyToManyPlugin, 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/language-filtered/languages-to-homes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | 5 | import type { WorkProSettings } from '../../../../../../../wechaty-settings/mod.js' 6 | 7 | import { bidirectionalMapper } from '../../bidirectional-mapper.js' 8 | 9 | const getLanguageToHomePlugin = (settings: WorkProSettings) => { 10 | const LanguageToHomePlugin = SourceToTargetRoomConnector({ 11 | blacklist: [ 12 | settings.mikeId, 13 | ], 14 | map: bidirectionalMapper, 15 | source: [ 16 | ...settings.rooms.wechatyDevelopers.chinese, 17 | ...settings.rooms.wechatyDevelopers.english, 18 | ], 19 | target: [ 20 | ...settings.rooms.wechatyDevelopers.home, 21 | ...settings.rooms.wechatyDevelopers.homeHq, 22 | ], 23 | }) 24 | return LanguageToHomePlugin 25 | } 26 | 27 | export { 28 | getLanguageToHomePlugin, 29 | } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/language-filtered/mod.ts: -------------------------------------------------------------------------------- 1 | export { getLanguageToHomePlugin } from './languages-to-homes.js' 2 | export { getHomeToChinesePlugin } from './homes-to-chinese.js' 3 | export { getHomeToEnglishPlugin } from './homes-to-english.js' 4 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/mod.ts: -------------------------------------------------------------------------------- 1 | export { getManyToManyPlugin } from './home-mixer.js' 2 | export { 3 | getHomeHqAnnouncingPlugin, 4 | getHomeHqCollectingPlugin, 5 | } from './home-hq.js' 6 | export { getHeadquartersBroadcastStationPlugin } from './broadcast-station.js' 7 | export { getSourceToTargetPlugin } from './polyglot-to-homes.js' 8 | 9 | export { 10 | getHomeToChinesePlugin, 11 | getHomeToEnglishPlugin, 12 | getLanguageToHomePlugin, 13 | } from './language-filtered/mod.js' 14 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-connectors/wechaty-developers-home/polyglot-to-homes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | SourceToTargetRoomConnector, 3 | } from 'wechaty-plugin-contrib' 4 | import type { WorkProSettings } from '../../../../../../wechaty-settings/mod.js' 5 | 6 | import { bidirectionalMapper } from '../bidirectional-mapper.js' 7 | 8 | /** 9 | * 10 | * OneToMany 11 | * 12 | */ 13 | const getSourceToTargetPlugin = (settings: WorkProSettings) => { 14 | const SourceToTargetPlugin = SourceToTargetRoomConnector({ 15 | map: bidirectionalMapper, 16 | source: [ 17 | ...Object.values(settings.rooms.polyglotUserGroup).flat(), 18 | ...Object.values(settings.rooms.puppetUserGroup).flat(), 19 | ], 20 | target: [ 21 | ...settings.rooms.wechatyDevelopers.homeHq, 22 | ...settings.rooms.wechatyDevelopers.home, 23 | ], 24 | }) 25 | return SourceToTargetPlugin 26 | } 27 | 28 | export { 29 | getSourceToTargetPlugin, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-invitation-accepter.ts: -------------------------------------------------------------------------------- 1 | import { RoomInvitationAccepter } from 'wechaty-plugin-contrib' 2 | 3 | export const RoomInvitationAccepterPlugin = RoomInvitationAccepter() 4 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/aidog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { repeat } from './config.js' 8 | 9 | const getAidogRoomInviter = (_: WorkProSettings) => { 10 | const aidogConfig: RoomInviterConfig = { 11 | password: [ 12 | /^aidog$/i, 13 | ], 14 | repeat, 15 | room: /^Youth fed the 5th dog$/i, 16 | welcome: '禁止在本群测试机器人。 注意:老群已满,此群为AiDog第五个新群', 17 | } 18 | 19 | const AidogRoomInviter = RoomInviter(aidogConfig) 20 | return AidogRoomInviter 21 | } 22 | 23 | export { 24 | getAidogRoomInviter, 25 | } 26 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/bot5.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | const getBot5RoomInviter = (settings: WorkProSettings) => { 8 | const bot5Config: RoomInviterConfig = { 9 | password: [ 10 | /^bot5$/i, 11 | /^BotFriday$/i, 12 | /^Bot Friday$/i, 13 | /^friday$/i, 14 | ], 15 | room: settings.rooms.bot5Club.rooms[1], // <- [1] is the current year member room 16 | rule: [ 17 | "I'm trying to invite you into Bot5, please read the manual first: https://www.bot5.club/manuals/newcomer", 18 | ], 19 | welcome: [ 20 | 'Welcome to join us!', 21 | ], 22 | } 23 | 24 | const Bot5RoomInviter = RoomInviter(bot5Config) 25 | return Bot5RoomInviter 26 | } 27 | 28 | export { 29 | getBot5RoomInviter, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/config.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | Contact, 3 | Room, 4 | } from 'wechaty' 5 | import type { 6 | talkers, 7 | } from 'wechaty-plugin-contrib' 8 | 9 | const repeat: talkers.ContactTalkerOptions = async (contact: Contact, room?: Room) => { 10 | await contact.say('You are already in our room: ' + await room?.topic()) 11 | } 12 | 13 | const WECHATY_DEVELOPERS_ROOM_RULES = [ 14 | 'Thanks for asking me to invite you for joining the "Wechaty Developers\' Home" WeChat Room!', 15 | 'Wechaty is a Conversational SDK for chatbot makers.', 16 | 'You can find our documentation at https://wechaty.js.org/docs/, and all archived discussions on https://gitter.im/wechaty/wechaty', 17 | 'Please introduce yourself after you join the room, cheers!', 18 | ] 19 | const WECHATY_DEVELOPERS_ROOM_WELCOME = [ 20 | ", welcome to join the Wechaty Python/Go/Java/PHP/.Net/Scala/Rust Developer's Home!", 21 | 'You can find Polyglot Wechaty docs at https://wechaty.js.org/docs/polyglot/ ,', 22 | 'Please go ahead to introduce yourself to the group.', 23 | ].join('\n') 24 | 25 | const WECHATY_DEVELOPERS_ROOM_RULES_CHINESE = [ 26 | '欢迎你加入 "Wechaty Developers\' Home" 微信群!', 27 | 'Wechaty 可以帮助你用 7 行代码接入聊天软件的实现自动化,', 28 | '官网 https://wechaty.js.org 可以查看新闻、博客和文档资料。', 29 | '请你在加入微信群后进行自我介绍,谢谢!', 30 | ] 31 | 32 | export { 33 | WECHATY_DEVELOPERS_ROOM_RULES, 34 | WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 35 | WECHATY_DEVELOPERS_ROOM_WELCOME, 36 | repeat, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/mod.ts: -------------------------------------------------------------------------------- 1 | export { getAidogRoomInviter } from './aidog.js' 2 | export { getBot5RoomInviter } from './bot5.js' 3 | export { getWechatyRoomInviter } from './wechaty.js' 4 | export { getWechatyChineseRoomInviter } from './wechaty-chinese.js' 5 | export { getWechatyEnglishRoomInviter } from './wechaty-english.js' 6 | export { getInviterPluginList } from './polyglot-wechaty.js' 7 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/wechaty-chinese.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 10 | } from './config.js' 11 | 12 | const getWechatyChineseRoomInviter = (settings: WorkProSettings) => { 13 | const wechatyChineseConfig: RoomInviterConfig = { 14 | password : [ 15 | /^wechaty chinese$/i, 16 | /^wechaty 中文$/i, 17 | /^中文$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.chinese, // DEVELOPERS_ROOM_ID_CHINESE, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES_CHINESE, 22 | welcome: [ 23 | ',欢迎你加入 Wechaty 中文开发者微信群!请发送一个简短的自我介绍向群友们做个介绍,谢谢!', 24 | ], 25 | } 26 | 27 | const WechatyChineseRoomInviter = RoomInviter(wechatyChineseConfig) 28 | return WechatyChineseRoomInviter 29 | } 30 | 31 | export { 32 | getWechatyChineseRoomInviter, 33 | } 34 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/wechaty-english.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES, 10 | WECHATY_DEVELOPERS_ROOM_WELCOME, 11 | } from './config.js' 12 | 13 | const getWechatyEnglishRoomInviter = (settings: WorkProSettings) => { 14 | const wechatyEnglishConfig: RoomInviterConfig = { 15 | password : [ 16 | /^wechaty english$/i, 17 | /^english$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.english, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES, 22 | welcome: WECHATY_DEVELOPERS_ROOM_WELCOME, 23 | } 24 | 25 | const WechatyEnglishRoomInviter = RoomInviter(wechatyEnglishConfig) 26 | return WechatyEnglishRoomInviter 27 | } 28 | 29 | export { 30 | getWechatyEnglishRoomInviter, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/room-inviters/wechaty.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RoomInviter, 3 | RoomInviterConfig, 4 | } from 'wechaty-plugin-contrib' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | 7 | import { 8 | repeat, 9 | WECHATY_DEVELOPERS_ROOM_RULES, 10 | WECHATY_DEVELOPERS_ROOM_WELCOME, 11 | } from './config.js' 12 | 13 | const getWechatyRoomInviter = (settings: WorkProSettings) => { 14 | const wechatyConfig: RoomInviterConfig = { 15 | password : [ 16 | /^wechaty$/i, 17 | /^plugin$/i, 18 | ], 19 | repeat, 20 | room: settings.rooms.wechatyDevelopers.home, 21 | rule: WECHATY_DEVELOPERS_ROOM_RULES, 22 | welcome: WECHATY_DEVELOPERS_ROOM_WELCOME, 23 | } 24 | 25 | const WechatyRoomInviter = RoomInviter(wechatyConfig) 26 | return WechatyRoomInviter 27 | } 28 | 29 | export { 30 | getWechatyRoomInviter, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/ai.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | 6 | // import { Chitchat } from '../../ml/chitchat.js' 7 | // import { Gpt } from '../../ml/gpt.js' 8 | import { Dreamily } from '../../../../../applications/ai-lib/dreamily.js' 9 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 10 | 11 | const getAiVorpalPlugin = (_settings: WorkProSettings) => { 12 | const aiConfig: WechatyVorpalConfig = { 13 | contact : true, 14 | mention : true, 15 | room : true, 16 | silent : true, 17 | 18 | use : [ 19 | // Chitchat(), 20 | // Gpt(), 21 | Dreamily(), 22 | ], 23 | } 24 | 25 | const AiVorpalPlugin = WechatyVorpal(aiConfig) 26 | return AiVorpalPlugin 27 | } 28 | 29 | export { 30 | getAiVorpalPlugin, 31 | } 32 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/chatops-ddr.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ddr, 7 | } from 'wechaty-vorpal-contrib' 8 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getDdrVorpalPlugin = (settings: WorkProSettings) => { 11 | const donutConfig: WechatyVorpalConfig = { 12 | contact : false, 13 | mention : false, 14 | room : [ 15 | settings.rooms.chatops.ddr, 16 | ], 17 | silent : true, 18 | 19 | use : [ 20 | Ddr(), 21 | ], 22 | } 23 | const DdrVorpalPlugin = WechatyVorpal(donutConfig) 24 | return DdrVorpalPlugin 25 | } 26 | 27 | export { 28 | getDdrVorpalPlugin, 29 | } 30 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/chatops-friday.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | Ding, 7 | Eval, 8 | Cash, 9 | UrlLink, 10 | Announce, 11 | Find, 12 | MathMaster, 13 | Version, 14 | Whoru, 15 | } from 'wechaty-vorpal-contrib' 16 | 17 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 18 | 19 | const getChatopsVorpalPlugin = (settings: WorkProSettings) => { 20 | const chatopsConfig: WechatyVorpalConfig = { 21 | contact : false, 22 | mention : false, 23 | room : settings.rooms.chatops.friday, 24 | silent : true, 25 | 26 | use: [ 27 | Ding(), 28 | Eval(), 29 | Cash(), 30 | UrlLink(), 31 | Announce(), 32 | Find(), 33 | MathMaster(), 34 | Whoru(), 35 | Version(), 36 | ], 37 | } 38 | 39 | const ChatopsVorpalPlugin = WechatyVorpal(chatopsConfig) 40 | return ChatopsVorpalPlugin 41 | } 42 | 43 | export { 44 | getChatopsVorpalPlugin, 45 | } 46 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/chatops-preangel.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | UrlLink, 7 | } from 'wechaty-vorpal-contrib' 8 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getPreAngelVorpalPlugin = (settings: WorkProSettings) => { 11 | const config: WechatyVorpalConfig = { 12 | contact : false, 13 | mention : false, 14 | room : settings.rooms.chatops.preangel, 15 | silent : true, 16 | 17 | use: [ 18 | UrlLink(), 19 | ], 20 | } 21 | const PreAngelVorpalPlugin = WechatyVorpal(config) 22 | return PreAngelVorpalPlugin 23 | } 24 | 25 | export { 26 | getPreAngelVorpalPlugin, 27 | } 28 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/contributors.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | UrlLink, 7 | Find, 8 | MathMaster, 9 | Version, 10 | Whoru, 11 | } from 'wechaty-vorpal-contrib' 12 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 13 | 14 | const getContributorsVorpalPlugin = (settings: WorkProSettings) => { 15 | const contributorsConfig: WechatyVorpalConfig = { 16 | contact : false, 17 | mention : true, 18 | room : settings.rooms.wechatyDevelopers.contributors, // CONTRIBUTORS_ROOM_ID, 19 | silent : true, 20 | 21 | use : [ 22 | UrlLink(), 23 | Find(), 24 | MathMaster(), 25 | Whoru(), 26 | Version(), 27 | ], 28 | } 29 | 30 | const ContributorsVorpalPlugin = WechatyVorpal(contributorsConfig) 31 | return ContributorsVorpalPlugin 32 | } 33 | 34 | export { 35 | getContributorsVorpalPlugin, 36 | } 37 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/direct-message.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import { 6 | MathMaster, 7 | Version, 8 | Whoru, 9 | } from 'wechaty-vorpal-contrib' 10 | 11 | import hackerNews from 'vorpal-hacker-news' 12 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 13 | 14 | const dmConfig: WechatyVorpalConfig = { 15 | contact : true, 16 | room : false, 17 | silent : true, 18 | 19 | use: [ 20 | MathMaster(), 21 | Whoru(), 22 | Version(), 23 | /** 24 | * https://github.com/vorpaljs/vorpal-hacker-news 25 | * hacker-news --length 3 26 | */ 27 | hackerNews, 28 | ], 29 | } 30 | 31 | const getDirectMessageVorpalPlugin = (_settings: WorkProSettings) => { 32 | const DirectMessageVorpalPlugin = WechatyVorpal(dmConfig) 33 | return DirectMessageVorpalPlugin 34 | } 35 | 36 | export { 37 | getDirectMessageVorpalPlugin, 38 | } 39 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/mod.ts: -------------------------------------------------------------------------------- 1 | import * as contributors from './contributors.js' 2 | import * as dm from './direct-message.js' 3 | import * as friday from './chatops-friday.js' 4 | import * as preangel from './chatops-preangel.js' 5 | import * as ddr from './chatops-ddr.js' 6 | import * as faq from './qnamaker.js' 7 | import * as ai from './ai.js' 8 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 9 | 10 | const getVorpalPlugins = (settings: WorkProSettings) => { 11 | const vorpalPluginList = [ 12 | ...Object.values(friday).map(get => get(settings)), 13 | ...Object.values(preangel).map(get => get(settings)), 14 | ...Object.values(contributors).map(get => get(settings)), 15 | ...Object.values(dm).map(get => get(settings)), 16 | ...Object.values(ddr).map(get => get(settings)), 17 | ...Object.values(faq).map(get => get(settings)), 18 | ...Object.values(ai).map(get => get(settings)), 19 | ] 20 | 21 | return vorpalPluginList 22 | } 23 | 24 | export { getVorpalPlugins } 25 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/plugins/vorpals/qnamaker.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WechatyVorpal, 3 | WechatyVorpalConfig, 4 | } from 'wechaty-vorpal' 5 | import type { WorkProSettings } from '../../../../../wechaty-settings/mod.js' 6 | // import { Faq } from 'wechaty-qnamaker' 7 | 8 | // import { 9 | // configChinese, 10 | // configEnglish, 11 | // } from '../qnamaker.js' 12 | 13 | const getFaqVorpalPlugin = (settings: WorkProSettings) => { 14 | const faqConfig: WechatyVorpalConfig = { 15 | contact : false, 16 | mention : false, 17 | room : [ 18 | settings.rooms.chatops.friday, 19 | ...settings.rooms.wechatyDevelopers.contributors, // CONTRIBUTORS_ROOM_ID, 20 | ], 21 | silent : true, 22 | 23 | use: [ 24 | // Faq([ 25 | // configChinese, 26 | // configEnglish, 27 | // ]), 28 | ], 29 | } 30 | 31 | const FaqVorpalPlugin = WechatyVorpal(faqConfig) 32 | return FaqVorpalPlugin 33 | } 34 | 35 | export { 36 | getFaqVorpalPlugin, 37 | } 38 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/set-handlers.ts: -------------------------------------------------------------------------------- 1 | import type { Wechaty } from 'wechaty' 2 | import type { WorkProSettings } from '../../../wechaty-settings/mod.js' 3 | 4 | import onError from './handlers/on-error.js' 5 | import { getOnFriendship } from './handlers/on-friendship.js' 6 | import onLogin from './handlers/on-login.js' 7 | import onLogout from './handlers/on-logout.js' 8 | import onMessage from './handlers/on-message.js' 9 | import onRoomInvite from './handlers/on-room-invite.js' 10 | import onRoomJoin from './handlers/on-room-join.js' 11 | import onRoomLeave from './handlers/on-room-leave.js' 12 | import onRoomTopic from './handlers/on-room-topic.js' 13 | import onScan from './handlers/on-scan.js' 14 | 15 | const setHandlers = (wechaty: Wechaty, settings: WorkProSettings) => { 16 | wechaty.on('error', onError) 17 | wechaty.on('friendship', getOnFriendship(settings)) 18 | wechaty.on('login', onLogin) 19 | wechaty.on('logout', onLogout) 20 | wechaty.on('message', onMessage) 21 | wechaty.on('room-invite', onRoomInvite) 22 | wechaty.on('room-join', onRoomJoin) 23 | wechaty.on('room-leave', onRoomLeave) 24 | wechaty.on('room-topic', onRoomTopic) 25 | wechaty.on('scan', onScan) 26 | } 27 | 28 | export { 29 | setHandlers, 30 | } 31 | -------------------------------------------------------------------------------- /src/wechaty-repository/builders/workpro/workpro-builder.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import * as WECHATY from 'wechaty' 3 | import { Brolog } from 'brolog' 4 | import { PuppetService } from 'wechaty-puppet-service' 5 | 6 | import { WorkProSettings } from '../../../wechaty-settings/mod.js' 7 | 8 | import { getPlugins } from './plugins/mod.js' 9 | import type { Builder } from '../builder.js' 10 | import { setHandlers } from './set-handlers.js' 11 | 12 | @Injectable() 13 | class WorkProBuilder implements Builder { 14 | 15 | constructor ( 16 | private readonly log: Brolog, 17 | public readonly settings: WorkProSettings, 18 | ) { 19 | this.log.verbose('WorkProBuilder', 'constructor(%s, %s)', 20 | settings.name, 21 | settings.token, 22 | ) 23 | } 24 | 25 | build () { 26 | this.log.verbose('WorkProBuilder', 'build()') 27 | 28 | const puppet = new PuppetService({ 29 | /** 30 | * Huan(20220228): workaround for disable TLS temporary for upgrade from Juzi.BOT 31 | */ 32 | tls: { 33 | disable: true, 34 | }, 35 | token: this.settings.token, 36 | }) 37 | 38 | const wechaty = WECHATY.WechatyBuilder.build({ 39 | name: this.settings.name, 40 | puppet, 41 | }) 42 | 43 | wechaty.use(getPlugins(this.settings)) 44 | setHandlers(wechaty, this.settings) 45 | 46 | return wechaty 47 | } 48 | 49 | } 50 | 51 | /** 52 | * Huan(20201201): Wechaty Developers' Home 9 53 | * R:10696051635011175 54 | */ 55 | 56 | // const oaTestChatOps = async (message: Message) => { 57 | // if (!workBot) { return } 58 | 59 | // const ROOM_ID = 'R:10696051746184005' // ChatOps - OA 60 | // const room = await workBot.Room.find({ id: ROOM_ID }) 61 | // if (!room) { 62 | // throw new Error('Room id: ' + ROOM_ID + ' not found') 63 | // } 64 | // await room.say(message.toString()) 65 | // } 66 | 67 | export { WorkProBuilder } 68 | -------------------------------------------------------------------------------- /src/wechaty-repository/mod.ts: -------------------------------------------------------------------------------- 1 | // import { 2 | // GitterBuilder, 3 | // OABuilder, 4 | // QqBuilder, 5 | // WeChatBuilder, 6 | // WhatsAppBuilder, 7 | // WXWorkBuilder, 8 | // } from './builders/mod.js' 9 | 10 | import { WechatyRepository } from './wechaty.repository.js' 11 | 12 | import { 13 | WechatyRepositoryModule, 14 | } from './wechaty-repository.module.js' 15 | 16 | export { 17 | WechatyRepositoryModule, 18 | WechatyRepository, 19 | // 20 | // GitterBuilder, 21 | // OABuilder, 22 | // QqBuilder, 23 | // WeChatBuilder, 24 | // WhatsAppBuilder, 25 | // WXWorkBuilder, 26 | } 27 | -------------------------------------------------------------------------------- /src/wechaty-repository/wechaty-repository.module.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sort-keys */ 2 | import { 3 | Module, 4 | } from '@nestjs/common' 5 | import { 6 | CqrsModule, 7 | } from '@nestjs/cqrs' 8 | 9 | import { InfrastructureModule } from '../infrastructures/infrastructure.module.js' 10 | import { WechatySettingsModule } from '../wechaty-settings/mod.js' 11 | 12 | import { WechatyBuilders } from './builders/mod.js' 13 | 14 | import { WechatyRepository } from './wechaty.repository.js' 15 | 16 | @Module({ 17 | imports: [ 18 | CqrsModule, 19 | InfrastructureModule, 20 | WechatySettingsModule, 21 | ], 22 | providers: [ 23 | WechatyRepository, 24 | ...WechatyBuilders, 25 | ], 26 | exports: [ 27 | WechatyRepository, 28 | ], 29 | }) 30 | export class WechatyRepositoryModule {} 31 | -------------------------------------------------------------------------------- /src/wechaty-settings/mod.ts: -------------------------------------------------------------------------------- 1 | export * from './settings/mod.js' 2 | 3 | export type { 4 | BotName, 5 | NamedInterface, 6 | } from './named-interface.js' 7 | export { WechatySettingsModule } from './wechaty-settings.module.js' 8 | -------------------------------------------------------------------------------- /src/wechaty-settings/named-interface.ts: -------------------------------------------------------------------------------- 1 | export type BotName = 2 | | 'Gitter' 3 | | 'OfficialAccount' 4 | | 'QQ' 5 | | 'WeChat' 6 | | 'WhatsApp' 7 | | 'WorkPro' 8 | 9 | export interface NamedInterface { 10 | name: BotName 11 | } 12 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/gitter/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | } from '@nestjs/common' 4 | import { Brolog } from 'brolog' 5 | 6 | import { EnvVar } from '../../../infrastructures/mod.js' 7 | 8 | import type { NamedInterface } from '../../named-interface.js' 9 | 10 | @Injectable() 11 | class GitterSettings implements NamedInterface { 12 | 13 | readonly name = 'Gitter' 14 | readonly wechatyRoomId = '573324fcc43b8c60197242bf' // 'https://gitter.im/wechaty/wechaty' 15 | 16 | readonly token: string 17 | 18 | constructor ( 19 | private readonly log: Brolog, 20 | envVar: EnvVar, 21 | ) { 22 | this.token = envVar 23 | .get('WECHATY_PUPPET_GITTER_TOKEN') 24 | .required(true) 25 | .asString() 26 | 27 | this.log.verbose('GitterSettings', 'constructor() %s token=%s, wechatyRoomId=%s', 28 | this.name, 29 | this.token, 30 | this.wechatyRoomId, 31 | ) 32 | } 33 | 34 | } 35 | 36 | export { 37 | GitterSettings, 38 | } 39 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/mod.ts: -------------------------------------------------------------------------------- 1 | import { GitterSettings } from './gitter/mod.js' 2 | import { OaSettings } from './oa/mod.js' 3 | import { QqSettings } from './qq/mod.js' 4 | import { WeChatSettings } from './wechat/mod.js' 5 | import { WhatsAppSettings } from './whatsapp/mod.js' 6 | import { WorkProSettings } from './workpro/mod.js' 7 | 8 | const WechatySettings = [ 9 | GitterSettings, 10 | OaSettings, 11 | QqSettings, 12 | WeChatSettings, 13 | WhatsAppSettings, 14 | WorkProSettings, 15 | ] 16 | 17 | export { 18 | WechatySettings, 19 | 20 | GitterSettings, 21 | OaSettings, 22 | QqSettings, 23 | WeChatSettings, 24 | WhatsAppSettings, 25 | WorkProSettings, 26 | } 27 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/oa/mod.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { Injectable } from '@nestjs/common' 3 | 4 | import { EnvVar } from '../../../infrastructures/mod.js' 5 | 6 | import type { NamedInterface } from '../../named-interface.js' 7 | 8 | @Injectable() 9 | class OaSettings implements NamedInterface { 10 | 11 | readonly name = 'OfficialAccount' 12 | 13 | readonly appId: string 14 | readonly appSecret: string 15 | readonly token: string 16 | readonly webhookProxyUrl: string 17 | 18 | constructor ( 19 | private readonly log: Brolog, 20 | envVar: EnvVar, 21 | ) { 22 | this.appId = envVar 23 | .get('HUAN_APP_ID') 24 | .required(true) 25 | .asString() 26 | 27 | this.appSecret = envVar 28 | .get('HUAN_APP_SECRET') 29 | .required(true) 30 | .asString() 31 | 32 | this.token = envVar 33 | .get('HUAN_TOKEN') 34 | .required(true) 35 | .asString() 36 | 37 | this.webhookProxyUrl = envVar 38 | .get('HUAN_WEBHOOK_PROXY_URL') 39 | .required(true) 40 | .asString() 41 | 42 | this.log.verbose('OaSettings', 'constructor() %s: appId=%s, webhookProxyUrl=%s', 43 | this.name, 44 | this.appId, 45 | this.webhookProxyUrl, 46 | ) 47 | } 48 | 49 | } 50 | 51 | export { 52 | OaSettings, 53 | } 54 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/qq/mod.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Injectable, 3 | } from '@nestjs/common' 4 | import { Brolog } from 'brolog' 5 | 6 | import { EnvVar } from '../../../infrastructures/mod.js' 7 | 8 | import type { NamedInterface } from '../../named-interface.js' 9 | 10 | @Injectable() 11 | export class QqSettings implements NamedInterface { 12 | 13 | readonly name = 'QQ' 14 | readonly wechatyRoomId = 'group_696864249' // Wechaty Developers' Home QQ 15 | 16 | readonly qq: number 17 | 18 | constructor ( 19 | private readonly log: Brolog, 20 | envVar: EnvVar, 21 | ) { 22 | this.qq = envVar 23 | .get('WECHATY_PUPPET_OICQ_QQ') 24 | .required(true) 25 | .asIntPositive() 26 | 27 | this.log.verbose('QqSettings', 'constructor() %s: qq=%s, wechatyRoomId=%s', 28 | this.name, 29 | this.qq, 30 | this.wechatyRoomId, 31 | ) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/wechat/rooms/bot5.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * [nextYear, current, ...previous] rooms 3 | */ 4 | const rooms = [ 5 | /** 6 | * [0]: Chair room (for the next year) 7 | * is only for those who has given their talk to the club in the current year 8 | */ 9 | '18013756151@chatroom', // BOT5 Club Alumni 2023 10 | 11 | /** 12 | * [1]: Member room (for the current year) 13 | * all messages in the latest room will be brodcasted to other rooms 14 | */ 15 | '19244336373@chatroom', // BOT5 Club Alumni 2022 16 | 17 | '18825797159@chatroom', // BOT5 Club Alumni 2021 18 | '18095776930@chatroom', // BOT5 Club Alumni 2020 19 | '17301175542@chatroom', // BOT5 Club Alumni 2019 20 | ] as const 21 | 22 | export { 23 | rooms, 24 | } 25 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/wechat/rooms/chatops.ts: -------------------------------------------------------------------------------- 1 | export const friday = '18131996049@chatroom' // 'ChatOps - Friday' 2 | export const preangel = '17237607145@chatroom' // 'ChatOps - PreAngel' 3 | export const heartbeat = '24980472405@chatroom' // ChatOps - Heartbeat' MIX 💖 4 | 5 | export const ddr = '18771413722@chatroom' // ChatOps - DDR 6 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/wechat/rooms/polyglot-user-group.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Polyglot Wechaty User Groups 3 | */ 4 | const dotnet = [ 5 | '18005545363@chatroom', // .NET Wechaty User Group 6 | ] 7 | const php = [ 8 | '19236545415@chatroom', // PHP Wechaty User Group 9 | ] 10 | 11 | const openapi = [ 12 | '19459143119@chatroom', // OpenAPI Wechaty User Group 13 | ] 14 | 15 | const java = [ 16 | '20062821521@chatroom', // Java Wechaty User Group 17 | ] 18 | 19 | const python = [ 20 | '20771403027@chatroom', // Python Wechaty User Group 21 | 'R:10696051131033249', // Huan(202205): the same room as above id 22 | ] 23 | 24 | const rust = [ 25 | '21063503320@chatroom', // Rust Wechaty User Group 26 | ] 27 | 28 | const go = [ 29 | '21153390249@chatroom', // Go Wechaty User Group 30 | ] 31 | 32 | const scala = [ 33 | '21366491788@chatroom', // Scala Wechaty User Group 34 | ] 35 | 36 | const typescript = [ 37 | '21148500489@chatroom', // TypeScript Wechaty User Group 38 | ] 39 | 40 | const grpc = [ 41 | '18000948607@chatroom', // Puppet Wechaty User Group 42 | ] 43 | 44 | export { 45 | dotnet, 46 | go, 47 | grpc, 48 | java, 49 | openapi, 50 | php, 51 | python, 52 | rust, 53 | scala, 54 | typescript, 55 | } 56 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/wechat/rooms/puppet-user-group.ts: -------------------------------------------------------------------------------- 1 | export const all = '18000948607@chatroom' // Puppet Wechaty User Group 2 | 3 | export const whatsapp = '18110358496@chatroom' // Wechaty Puppet WhatsApp 4 | export const walnut = '17754743068@chatroom' // Wechaty Puppet Walnut 5 | export const itchat = '20764206744@chatroom' // Wechaty Puppet Itchat 6 | export const xp = '21013901541@chatroom' // Wechaty Puppet XP 7 | export const oicq = '24082599405@chatroom' // Wechaty Puppet OICQ 8 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/wechat/rooms/wechaty-developers.ts: -------------------------------------------------------------------------------- 1 | const broadcastStation = [ 2 | '20504723205@chatroom', // Wechaty Broadcasting 3 | ] 4 | 5 | const homeHq = [ 6 | '17275396976@chatroom', // Wechaty Developers' HQ - WDHQ 7 | ] 8 | 9 | const monitor = [ 10 | '24394489499@chatroom', // Wechaty Developers' Home 0 11 | ] 12 | 13 | const home = [ 14 | '19367909379@chatroom', // Wechaty Developers' Home 9 (former Polyglot Wechaty) 15 | '18490919725@chatroom', // Wechaty Developers' Home 8 16 | '18171595067@chatroom', // Wechaty Developers' Home 7 17 | '24113855649@chatroom', // Wechaty Developers' Home 6 18 | '19112581505@chatroom', // Wechaty Developers' Home 5 19 | '22396239792@chatroom', // Wechaty Developers' Home 4 20 | '4335801863@chatroom', // Wechaty Developers' Home 3 21 | '5729603967@chatroom', // Wechaty Developers' Home 2 22 | '7582163093@chatroom', // Wechaty Developers' Home 1 23 | ] 24 | 25 | const chinese = [ 26 | '20766978781@chatroom', // Wechaty Developers' Home Chinese 27 | ] 28 | 29 | const english = [ 30 | '20517298932@chatroom', // Wechaty Developers' Home English 31 | ] 32 | 33 | const contributors = [ 34 | '6719192413@chatroom', // Wechaty Contributors 35 | ] 36 | 37 | const summer = [ 38 | '18324919941@chatroom', // Summer of Wechaty - SoW 39 | ] 40 | 41 | export { 42 | chinese, 43 | contributors, 44 | english, 45 | broadcastStation, 46 | home, 47 | homeHq, 48 | monitor, 49 | summer, 50 | } 51 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/whatsapp/mod.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common' 2 | import { Brolog } from 'brolog' 3 | 4 | // import { EnvVar } from '../../../infrastructures/mod.js' 5 | 6 | import type { NamedInterface } from '../../named-interface.js' 7 | 8 | @Injectable() 9 | class WhatsAppSettings implements NamedInterface { 10 | 11 | readonly name = 'WhatsApp' 12 | readonly wechatyRoomId = '120363039693955850@g.us' 13 | 14 | constructor ( 15 | private log: Brolog, 16 | // envVar: EnvVar, 17 | ) { 18 | this.log.verbose('WhatsAppSettings', 'constructor() %s', 19 | this.name, 20 | ) 21 | } 22 | 23 | } 24 | 25 | export { 26 | WhatsAppSettings, 27 | } 28 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/mod.ts: -------------------------------------------------------------------------------- 1 | import { Brolog } from 'brolog' 2 | import { Injectable } from '@nestjs/common' 3 | 4 | import { EnvVar } from '../../../infrastructures/mod.js' 5 | import type { NamedInterface } from '../../named-interface.js' 6 | 7 | import * as polyglotUserGroup from './rooms/polyglot-user-group.js' 8 | import * as puppetUserGroup from './rooms/puppet-user-group.js' 9 | import * as wechatyDevelopers from './rooms/wechaty-developers.js' 10 | import * as bot5Club from './rooms/bot5.js' 11 | import * as chatops from './rooms/chatops.js' 12 | 13 | @Injectable() 14 | class WorkProSettings implements NamedInterface { 15 | 16 | readonly name = 'WorkPro' 17 | readonly heartbeatRoomId = 'R:10696051718181051' // ChatOps - Heartbeat 💖 18 | readonly chatOpsRoomId = 'R:10696051718181051' // ChatOps - Heartbeat 💖 19 | 20 | // To-be-updated 21 | readonly mikeId = 'wxid_a8d806dzznm822' // Mike BO 22 | 23 | readonly token: string 24 | 25 | readonly rooms = { 26 | bot5Club, 27 | chatops, 28 | polyglotUserGroup, 29 | puppetUserGroup, 30 | wechatyDevelopers, 31 | } as const 32 | 33 | constructor ( 34 | private readonly log: Brolog, 35 | envVar: EnvVar, 36 | ) { 37 | this.token = envVar 38 | .get('WECHATY_PUPPET_SERVICE_TOKEN_WORKPRO') 39 | .required(true) 40 | .asString() 41 | 42 | this.log.verbose('WorkProSettings', 'constructor() %s: token=%s chatOpsRoomId=%s', 43 | this.name, 44 | this.token, 45 | this.chatOpsRoomId, 46 | ) 47 | } 48 | 49 | } 50 | 51 | export { 52 | WorkProSettings, 53 | } 54 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/rooms/bot5.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * [nextYear, current, ...previous] rooms 3 | */ 4 | const rooms = [ 5 | /** 6 | * [0]: Chair room (for the next year) 7 | * is only for those who has given their talk to the club in the current year 8 | */ 9 | 'R:10696051795072507', // BOT5 Club Alumni 2023 10 | 11 | /** 12 | * [1]: Member room (for the current year) 13 | * all messages in the latest room will be brodcasted to other rooms 14 | */ 15 | 'R:10740862767053411', // BOT5 Club Alumni 2022 16 | ] as const 17 | 18 | export { 19 | rooms, 20 | } 21 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/rooms/chatops.ts: -------------------------------------------------------------------------------- 1 | export const friday = 'R:10836177343918215' // 'ChatOps - Friday BOT' 2 | 3 | export const preangel = 'R:10744360171540010' // 'ChatOps - PreAngel' 4 | export const heartbeat = 'R:10696051718181051' // ChatOps - Heartbeat 💖 5 | 6 | export const ddr = 'R:10944579689184880' // ChatOps - DDR 7 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/rooms/polyglot-user-group.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Polyglot Wechaty User Groups 3 | */ 4 | const dotnet = [ 5 | 'R:10696051131033855', // .NET Wechaty User Group 6 | ] 7 | 8 | const php = [ 9 | 'R:10696052125028220', // PHP Wechaty User Group 10 | ] 11 | 12 | const openapi = [ 13 | 'R:10696053234026039', // OpenAPI Wechaty User Group 14 | ] 15 | 16 | const java = [ 17 | 'R:10696052125026819', // Java Wechaty User Group 18 | ] 19 | 20 | const python = [ 21 | 'R:10696051131033249', // Python Wechaty User Group 22 | ] 23 | 24 | const rust = [ 25 | 'R:10696052125027951', // Rust Wechaty User Group 26 | ] 27 | 28 | const go = [ 29 | 'R:10696052125027462', // Go Wechaty User Group 30 | ] 31 | 32 | // const scala = [ 33 | // '21366491788@chatroom', // Scala Wechaty User Group 34 | // ] 35 | 36 | const typescript = [ 37 | 'R:10696051119052679', // TypeScript Wechaty User Group 38 | ] 39 | 40 | export { 41 | dotnet, 42 | go, 43 | java, 44 | openapi, 45 | php, 46 | python, 47 | rust, 48 | // scala, 49 | typescript, 50 | } 51 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/rooms/puppet-user-group.ts: -------------------------------------------------------------------------------- 1 | export const all = 'R:10696050883036241' // Puppet Wechaty User Group 2 | 3 | export const whatsapp = 'R:10757870526285271' // Wechaty Puppet WhatsApp 4 | export const walnut = 'R:10884654612175839' // Wechaty Puppet Walnut 5 | export const xp = 'R:10846063578603578' // Wechaty Puppet XP 6 | export const oicq = 'R:10696050585018440' // Wechaty Puppet OICQ 7 | -------------------------------------------------------------------------------- /src/wechaty-settings/settings/workpro/rooms/wechaty-developers.ts: -------------------------------------------------------------------------------- 1 | const broadcastStation = [ 2 | 'R:10696049890023057', // Wechaty Broadcast Station 3 | ] 4 | 5 | const homeHq = [ 6 | 'R:10736536406152502', // Wechaty Developers' Home 7 | ] 8 | 9 | const monitor = [ 10 | 'R:10699121424455226', // Wechaty Developers' Home 0 11 | ] 12 | 13 | const home = [ 14 | 'R:10735648038885101', // Wechaty Developers’s Home 10 (202302 Friday Bot / Huan) 15 | 'R:10752005378712795', // Wechaty Developers' Home 9 (former Polyglot Wechaty) 16 | 'R:10706326751384512', // Wechaty Developers' Home 8 17 | 'R:10745797260099492', // Wechaty Developers' Home 7 18 | 'R:10924600702550989', // Wechaty Developers' Home 6 19 | 'R:10855009180534640', // Wechaty Developers' Home 5 20 | 'R:10787187106053549', // Wechaty Developers' Home 4 21 | 'R:10939022299083370', // Wechaty Developers' Home 3 22 | 'R:10921234531988235', // Wechaty Developers' Home 2 23 | 'R:10708787256496235', // Wechaty Developers' Home 1 24 | ] 25 | 26 | const chinese = [ 27 | 'R:10797730855547206', // Wechaty Developers' Home Chinese 28 | ] 29 | 30 | const english = [ 31 | 'R:10782491303849571', // Wechaty Developers' Home English 32 | ] 33 | 34 | const contributors = [ 35 | 'R:10856857550452201', // Wechaty Contributors 36 | ] 37 | 38 | const summer = [ 39 | 'R:10781326903921777', // Summer of Wechaty - SoW 40 | ] 41 | 42 | export { 43 | chinese, 44 | contributors, 45 | english, 46 | broadcastStation, 47 | home, 48 | homeHq, 49 | monitor, 50 | summer, 51 | } 52 | -------------------------------------------------------------------------------- /src/wechaty-settings/wechaty-settings.module.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable sort-keys */ 2 | import { 3 | Module, 4 | } from '@nestjs/common' 5 | 6 | import { InfrastructureModule } from '../infrastructures/mod.js' 7 | 8 | import { WechatySettings } from './settings/mod.js' 9 | 10 | @Module({ 11 | imports: [ 12 | InfrastructureModule, 13 | ], 14 | providers: [ 15 | ...WechatySettings, 16 | ], 17 | exports: [ 18 | ...WechatySettings, 19 | ], 20 | }) 21 | export class WechatySettingsModule {} 22 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@chatie/tsconfig", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | }, 6 | "exclude": [ 7 | "node_modules/", 8 | "dist/", 9 | "tests/fixtures/", 10 | ], 11 | "include": [ 12 | "bin/*.ts", 13 | "examples/**/*.ts", 14 | "scripts/**/*.ts", 15 | "src/**/*.ts", 16 | "tests/**/*.spec.ts", 17 | ], 18 | "ts-node": { 19 | "transpileOnly": true, 20 | "transpiler": "", 21 | }, 22 | } 23 | --------------------------------------------------------------------------------