├── .gitattributes
├── .github
└── workflows
│ ├── main.yml
│ └── rust.yml
├── .gitignore
├── COPYING.md
├── CREDITS.md
├── Cargo.lock
├── Cargo.toml
├── HISTORY.md
├── LOCAL_WINDOWS_10.md
├── README.md
├── RELEASE-NOTES-1.0.md
├── SECURITY.md
├── avatar.webp
├── bridge-cmd.drawio
├── bridge-design-old.drawio
├── bridge-design.drawio
├── config.simple.json
├── data
└── .gitkeep
├── docs
└── discord
│ ├── images
│ ├── 0.png
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ └── 5.png
│ └── set_discord_channel_webhook.md
├── mirai_rs
├── Cargo.toml
└── src
│ ├── adapter
│ ├── http_adapter.rs
│ └── mod.rs
│ ├── lib.rs
│ ├── message.rs
│ ├── mirai_http.rs
│ ├── model
│ ├── group.rs
│ └── mod.rs
│ └── response.rs
├── package-lock.json
├── package.json
├── rustfmt.toml
├── server.ts
├── src
├── bridge
│ ├── bridge_message.rs
│ ├── config.rs
│ ├── manager
│ │ ├── message_manager.rs
│ │ ├── mod.rs
│ │ └── user_manager.rs
│ ├── mod.rs
│ ├── pojo
│ │ ├── form
│ │ │ ├── bridge_message_ref_message_form.rs
│ │ │ ├── bridge_message_save_form.rs
│ │ │ ├── bridge_send_message_form.rs
│ │ │ ├── bridge_user_save_form.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ └── po
│ │ │ ├── bridge_message_po.rs
│ │ │ ├── bridge_user_ref_po.rs
│ │ │ └── mod.rs
│ └── user.rs
├── bridge_cmd
│ ├── bridge_client.rs
│ ├── mod.rs
│ └── process
│ │ ├── bind_proc.rs
│ │ └── mod.rs
├── bridge_data.rs
├── bridge_dc
│ ├── bridge_client.rs
│ ├── handler.rs
│ └── mod.rs
├── bridge_log.rs
├── bridge_qq
│ ├── group_message_id.rs
│ ├── handler.rs
│ └── mod.rs
├── bridge_tg
│ └── mod.rs
├── cmd_adapter.rs
├── config.rs
├── logger.rs
├── main.rs
├── test_dc.rs
├── test_mirai.rs
├── test_regex.rs
├── test_reqwest.rs
└── utils.rs
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js eol=lf
2 | *.json eol=lf
3 | *.md eol=lf
4 | *.rs eol=lf
5 | *.yml eol=lf
6 | *.webp binary
7 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Rust-static-build
2 | on:
3 | push:
4 | branches: [ _main ]
5 | pull_request:
6 | branches: [ _main ]
7 | env:
8 | CARGO_TERM_COLOR: always
9 | BUILD_TARGET: x86_64-unknown-linux-musl
10 | BINARY_NAME: message_bridge
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Build-musl
17 | uses: jean-dfinity/rust-musl-action@master
18 | with:
19 | args: cargo +nightly build --target $BUILD_TARGET --release
20 | - uses: actions/upload-artifact@v2
21 | with:
22 | name: ${{ env.BINARY_NAME }}
23 | path: target/x86_64-unknown-linux-musl/release/${{ env.BINARY_NAME }}*
24 |
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: actions-rs/toolchain@v1.0.6
18 | with:
19 | toolchain: nightly
20 | override: true
21 | - run: cargo +nightly build --release --verbose
22 |
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | # compiled output
3 | /dist
4 | /node_modules
5 |
6 | # Logs
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 | lerna-debug.log*
13 |
14 | # OS
15 | .DS_Store
16 |
17 | # Tests
18 | /coverage
19 | /.nyc_output
20 |
21 | # IDEs and editors
22 | /.idea
23 | .project
24 | .classpath
25 | .c9/
26 | *.launch
27 | .settings/
28 | *.sublime-workspace
29 |
30 | # IDE - VSCode
31 | .vscode/*
32 | !.vscode/settings.json
33 | !.vscode/tasks.json
34 | !.vscode/launch.json
35 | !.vscode/extensions.json
36 |
37 | go-cqhttp
38 |
39 | /cache
40 | /config.json
41 | /config.dev.json
42 | /config.prod.json
43 | # 数据
44 | /data/*.json
45 |
46 | # qq数据
47 | device.json
48 | session.token
49 | qrcode.png
50 |
51 | # tg数据
52 | tg.pack.*/
53 | telegram.session
54 |
--------------------------------------------------------------------------------
/COPYING.md:
--------------------------------------------------------------------------------
1 | # License and copyright information
2 |
3 | ## License
4 |
5 | message_bridge_rs is licensed under the terms of the ISC License. Derivative
6 | works and later versions of the code must be attributed.
7 |
8 | For the full text of the license, see https://www.isc.org/licenses/ or
9 | **ISC License** below.
10 |
11 | ## Copyright owners
12 |
13 | message_bridge_rs contributors, including those listed in the CREDITS file, hold
14 | the copyright to this work.
15 |
16 | ## Additional license information
17 |
18 | Some components of message_bridge_rs imported from other projects may be under
19 | other Free and Open Source, or Free Culture, licenses. Specific details of their
20 | licensing information can be found in those components.
21 |
22 | ## ISC LICENSE
23 |
24 | ISC License
25 |
26 | Copyright (c) message_bridge_rs Project
27 |
28 | Permission to use, copy, modify, and/or distribute this software for any purpose
29 | with or without fee is hereby granted, provided that the above copyright notice
30 | and this permission notice appear in all copies.
31 |
32 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
33 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
34 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
35 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
36 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
37 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
38 | THIS SOFTWARE.
39 |
--------------------------------------------------------------------------------
/CREDITS.md:
--------------------------------------------------------------------------------
1 | # Credits
2 |
3 | message_bridge_rs is an open-source project. We would like to recognize the
4 | following names for their contribution to the project.
5 |
6 | ## Contributors
7 |
8 | * rabbitkiller-dev (rabbitkiller)
9 | * RogenDong (6uopdong)
10 |
11 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "message_bridge_rs"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | time = "0.3.14"
10 | color-eyre = "0.6.2"
11 | tracing = "0.1.36"
12 | tracing-error = "0.2.0"
13 | tracing-appender = "0.2.2"
14 | tracing-subscriber = { version = "0.3.16", features = [
15 | "fmt",
16 | "env-filter",
17 | "local-time",
18 | "time",
19 | ] }
20 | lazy_static = "1.4.0"
21 | reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
22 | tokio = { version = "1.14.0", features = ["full"] }
23 | serde_json = "1.0"
24 | serde = { version = "1.0", features = ["derive"] }
25 | chrono = "0.4.22"
26 | regex = "1.6.0"
27 | mime = "0.3.16"
28 | mime_guess = "2.0.4"
29 | md5 = "0.7.0"
30 | image-base64 = "0.1.0"
31 | anyhow = "1.0.69"
32 | proc_qq = { git = "https://github.com/niuhuan/rust_proc_qq.git", rev = "dda3d45" }
33 | teleser = { git = "https://github.com/niuhuan/teleser-rs.git", branch = "patched", features = ["proxy"] }
34 |
35 | [dependencies.serenity]
36 | default-features = false
37 | features = ["client", "gateway", "rustls_backend", "model"]
38 | version = "0.11.5"
39 |
40 | [dependencies.uuid]
41 | version = "1.1.2"
42 | features = [
43 | "v4", # Lets you generate random UUIDs
44 | "fast-rng", # Use a faster (but still sufficiently random) RNG
45 | "macro-diagnostics", # Enable better diagnostics for compile-time UUIDs
46 | ]
47 |
48 | [dependencies.url]
49 | version = "^2.1"
50 | features = ["serde"]
51 |
52 | [dependencies.clap]
53 | version = "^4.1"
54 | features = ["derive"]
55 |
56 | [dev-dependencies]
57 | tokio-test = "*"
58 |
59 | [workspace]
60 | members = []
61 |
62 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | Change notes from older releases. For current info, see RELEASE-NOTES-1.0.
2 |
3 | # message_bridge_rs 0.1
4 |
5 | ## message_bridge_rs 0.1.0
6 |
7 | This is the version of the initial commit of message_bridge_rs.
8 |
9 | This is also the last version of message_bridge_rs 0.1.x. Next version will be
10 | message_bridge_rs 1.0.x.
11 |
12 | ### Changes of the initial commit of message_bridge_rs
13 |
14 | * feat: Add Mirai verify, bind APIs
15 |
--------------------------------------------------------------------------------
/LOCAL_WINDOWS_10.md:
--------------------------------------------------------------------------------
1 | # Windows10 本地运行
2 |
3 | ## 环境要求
4 | 1. 科学上网
5 | 2. [NodeJs (v14 以上)](https://nodejs.org/en)
6 | 3. Rust: `curl https://sh.rustup.rs -sSf | sh`
7 | 4. Git
8 |
9 | > 安装完成后接一下操作
10 |
11 | 1. 命令行运行
12 | ```shell
13 | # rust默认使用nightly
14 | rustup default nightly
15 |
16 | # 克隆本项目
17 | git clone
18 | ```
19 |
20 | 2. 克隆本项目
21 |
22 | !TODO
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 简介
2 | 通过注册Bot机器人, 实现不同平台用户之间的消息进行同步
3 |
4 | ((TODO: [补上示例图片]))
5 |
6 | ## 联系方式
7 | - Discord频道: https://discord.gg/9Xv4YsxPSA
8 | - QQ群:https://jq.qq.com/?_wv=1027&k=D8ymzW7M
9 |
10 | 欢迎使用和部署, 加入上方联系方式也可以进行体验
11 |
12 | ## 环境要求
13 | 1. 科学上网
14 | 2. [NodeJs (v14 以上)](https://nodejs.org/en)
15 | 3. Rust: `curl https://sh.rustup.rs -sSf | sh`
16 | 3. Git
17 |
18 | ## Windows10 本地运行
19 | 环境配置
20 |
21 | 1. 配置rust默认使用nightly运行
22 | > rustup default nightly
23 |
24 | 2. 配置config.json
25 |
26 | > 复制config.simple.json文件并重命令为config.json
27 |
28 | 3. 启动解释discord消息服务
29 | > npm install
30 | > npm start
31 |
32 | 4. 运行桥服务
33 | > cargo run
34 |
35 | ## CenterOS
36 | > 安装命令参考
37 |
38 | 1. [Git](https://git-scm.com/download/linux): 命令: `yum install git`
39 | 2. NodeJs (v14 以上): 命令自行baidu
40 | 3. 全局安装pm2: `npm install -g pm2`
41 | 4. [Rust + Cargo](https://forge.rust-lang.org/infra/other-installation-methods.html): 命令: `curl https://sh.rustup.rs -sSf | sh`
42 | 5. 配置文件: `cp config.simple.json config.json` 配置说明: CONFIG.md ((TODO: 说明config.json怎么配置))
43 |
44 | ## CenterOS 部署方式
45 |
46 | > ps: 以上环境请务必自选解决
47 |
48 | ```shell
49 | > git clone https://github.com/rabbitkiller-dev/message_bridge_rs
50 | > npm install
51 |
52 | ## 启动 (pm2进程守护)
53 | > npm run build
54 | > pm2 start server.js --name bridge_js
55 | > pm2 start "cargo run" --name bridge_js
56 | ```
57 |
58 |
59 | ## 功能情况
60 |
61 | ((TODO))
62 |
63 | ### 指令方面
64 | - !help
65 | - !关联
66 | - !解除关联
67 |
68 | #### 1. 关联
69 | 1. 第一步: 发送指令, bot会返回验证码, 记住后在另一个平台回复
70 | > !关联
71 | 2. 第二步: 在另一个平台上, 使用自己的账号, 发送指令
72 | > !关联 xxxxxx
73 | 3. 第三步: 返回原来的平台, 进行关联确认
74 | > !确认关联
75 |
76 | ### 2.0 遗留项
77 | 1. qq群自动审批
78 | 2. 桥后台配置界面
79 | 2. bot命令搜图
80 | 2. bot命令关联qq与dc用户
81 |
82 |
83 | ```
84 | !帮助
85 | !ping
86 | !确认绑定
87 | !解除绑定
88 | !查看绑定状态
89 | !来点[搜图]
90 | !废话生成器
91 | !猜数字游戏
92 |
93 | 管理员:
94 | !服务器状态
95 | !重启
96 | !查看所有成员绑定关系
97 | !绑定成员关联 [用户名] [用户名]
98 | !解除成员关联 [用户名]
99 |
100 | ```
101 |
102 |
103 |
--------------------------------------------------------------------------------
/RELEASE-NOTES-1.0.md:
--------------------------------------------------------------------------------
1 | # message_bridge_rs 1.0
2 |
3 | ## message_bridge_rs 1.0.0-alpha.4
4 |
5 | THIS IS NOT A RELEASE YET
6 |
7 | ### Changes since message_bridge_rs 1.0.0-alpha.3
8 |
9 | * (bug #17) docs: Update RELEASE-NOTES for 1.0.0-alpha.1, 1.0.0-alpha.2,
10 | 1.0.0-alpha.3
11 |
12 | ## message_bridge_rs 1.0.0-alpha.3
13 |
14 | message_bridge_rs 1.0.0-alpha.3 is an alpha-quality development branch.
15 |
16 | See bug #15, #16.
17 |
18 | ### Changes since message_bridge_rs 1.0.0-alpha.2
19 |
20 | * feat: Handle file extension issues - part 2
21 | * feat: Handle file extension issues - part 3
22 |
23 | ## message_bridge_rs 1.0.0-alpha.2
24 |
25 | message_bridge_rs 1.0.0-alpha.2 is an alpha-quality development branch.
26 |
27 | See bug #14.
28 |
29 | ### Changes since message_bridge_rs 1.0.0-alpha.1
30 |
31 | * (bug #7) fix: Fix CI 'act-build'
32 | * (bug #8, #12) docs: Update RELEASE-NOTES
33 |
34 | ## message_bridge_rs 1.0.0-alpha.1
35 |
36 | message_bridge_rs 1.0.0-alpha.1 is an alpha-quality development branch.
37 |
38 | See bug #13.
39 |
40 | ### Changes since message_bridge_rs 0.1.0
41 |
42 | * feat: Support forward Tencent QQ group messages to Discord
43 | * feat: Complete bridge encapsulation
44 | * feat: Implement forward bridge messages to Tencent QQ groups
45 | * feat: Add bridge user and log related stuffs
46 | * feat: Support send user avatar when forwarding message
47 | * (bug #2) feat: Add command 'channel'
48 | * feat: Support convert Tencent QQ group message images to bridge images and
49 | forward bridge images to Discord
50 | * feat: Support forward Discord image attachments to Tencent QQ group
51 | * feat: Implement read & write user binding data (sync)
52 | * perf: Extend bridge::User
53 | * fix: Fix cmd_adapter errors
54 | * feat: Add Discord and Tencent QQ group user query and tests
55 | * refactor: Adjust formats for map-type data
56 | * (bug #3) pref: Command enumeration
57 | * (bug #4) feat: Add command 'confirm bind'
58 | * pref: Split message handling
59 | * (bug #4) pref: Unify user ID type
60 | * feat: Parse Discord message and identify cross-platform user mentions by using
61 | JS library
62 | * (bug #4) feat: Update command 'bind'
63 | * (bug #4) pref: Improve data structures for map-type data
64 | * fix: Temporarily disable command responses
65 | * feat: Support mentioning Discord users from Tencent QQ grops
66 | * (bug #5) feat: Sync CMD command responses
67 | * ci: Create rust.yml
68 | * feat: Support context for Mirai events
69 | * (bug #4) fix: Check, create data directories before read/write binding data
70 | * (bug #4) fix: Return failure-type responses when failed to save binding data
71 | * (bug #4) pref: Simplify query parameters for user binding queries
72 | * ci: Create main.yml
73 | * ci: Update rust.yml - remove 'cargo test'
74 | * ci: Update rust.yml - add 'upload-artifact'
75 | * ci: Test builds
76 | * ci: Use releases mode for builds
77 | * ci: Upload build artifacts
78 | * feat: Use tracing to record logs
79 | * pref: Add, adjust tracing logs for bridge
80 | * feat: Add dependency 'lazy_static'
81 | * feat: Add bridge message history
82 | * feat: Record relationships between sent messages and forward messages
83 | * (bug #4) feat: Add command 'unbind'
84 | * feat: Implement BitOr (bitwise operation 'OR') for platform enumeration
85 | * feat: Change reqwest - use rustls
86 | * pref: Replace println to logs
87 | * (bug #8) docs: Add COPYING, CREDITS, HISTORY, RELEASE-NOTES, SECURITY
88 | * (bug #7) feat: Migrate QQ group functionalities from MCL to RICQ
89 | * (bug #7) feat: Complete Tencent QQ group bridge & support improved message
90 | images to bridge - part 1
91 | * (bug #7) feat: Complete Tencent QQ group bridge & support improved message
92 | images to bridge - part 2
93 | * (bug #7) feat: Add user_manager for bridge user management
94 | * (bug #4, #7) feat: Configure global service 'user_manager' & complete user
95 | mention handling in Discord and Tencent QQ groups
96 | * (bug #4, #7) feat: Complete user relation query feature & implement fetch
97 | cross-platform user mention for bonded accounts for Discord and Tencent QQ
98 | group
99 | * (bug #7) feat: Add binary expression macro
100 | * (bug #7) feat: Adjust variable name & update module structures - part 1
101 | * (bug #7) feat: Adjust variable name & update module structures - part 2
102 | * (bug #7) docs: Update README.md
103 | * (bug #7) feat: Remove feature 'message history'
104 | * (bug #7) fix: Log level too low when not being set in environment varibles
105 | * (bug #7) pref: Clean up modules
106 | * (bug #7) fix: Use nightly for CI 'act-build' for GitHub
107 | * feat: Handle file extension issues
108 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security information
2 |
3 | message_bridge_rs takes security very seriously. If you believe you have found a
4 | security issue, please report it at
5 |
6 |
--------------------------------------------------------------------------------
/avatar.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/avatar.webp
--------------------------------------------------------------------------------
/bridge-cmd.drawio:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/bridge-design-old.drawio:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bridge-design.drawio:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/bridge-design.drawio
--------------------------------------------------------------------------------
/config.simple.json:
--------------------------------------------------------------------------------
1 | {
2 | "qqConfig": {
3 | "botId": # 机器人账号 #,
4 | "password": # 机器人登录密码 16位MD5 #,
5 | "version": # 登录协议 iPad MacOS QiDian AndroidPhone AndroidWatch #,
6 | "auth": # 无token时的认证方式 qr(二维码) pwd(账号+密码) #
7 | },
8 | "discordConfig": {
9 | "botId": # 机器人的id(u64) #,
10 | "botToken": # 机器人的token #
11 | },
12 | "telegramConfig": {
13 | "apiId": # apiId(i32) # ,
14 | "apiHash": # apiHash # ,
15 | "botToken": # botToken #
16 | },
17 | "bridges": [
18 | {
19 | "discord": {
20 | "id": # 频道的Webhook id(u64) #,
21 | "token": # 频道的token #,
22 | "channelId": # 频道的id(u64) #
23 | },
24 | "tgGroup": # telegram群号 #,
25 | "qqGroup": # qq群号(u64) #,
26 | "enable": true
27 | }
28 | ]
29 | }
--------------------------------------------------------------------------------
/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/data/.gitkeep
--------------------------------------------------------------------------------
/docs/discord/images/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/0.png
--------------------------------------------------------------------------------
/docs/discord/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/1.png
--------------------------------------------------------------------------------
/docs/discord/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/2.png
--------------------------------------------------------------------------------
/docs/discord/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/3.png
--------------------------------------------------------------------------------
/docs/discord/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/4.png
--------------------------------------------------------------------------------
/docs/discord/images/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yefu24324/message_bridge_rs/699f4c7c1bc2f4ecb9b50a42ff13faebf8694d44/docs/discord/images/5.png
--------------------------------------------------------------------------------
/docs/discord/set_discord_channel_webhook.md:
--------------------------------------------------------------------------------
1 | 1.
2 | 
3 |
4 | 
5 |
6 | 
7 |
8 | 
9 |
10 | 
11 |
12 | https://discord.com/api/webhooks/1093074789809262662/n2WrC4sIHASKeklYFos6Usr3c2j3k7_v0O2USPQ8i0Ac27hk8TrWLi-z1NxR7TaFMMsY
13 |
--------------------------------------------------------------------------------
/mirai_rs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "mirai_rs"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | reqwest = { version = "0.11", features = ["json"] }
10 | tokio = { version = "1.14.0", features = ["full"] }
11 | serde_json = "1.0"
12 | serde = { version = "1.0", features = ["derive"] }
13 |
14 | [dependencies.async-trait]
15 | version = "0.1.9"
--------------------------------------------------------------------------------
/mirai_rs/src/adapter/http_adapter.rs:
--------------------------------------------------------------------------------
1 | pub struct HttpAdapter {}
2 |
--------------------------------------------------------------------------------
/mirai_rs/src/adapter/mod.rs:
--------------------------------------------------------------------------------
1 | mod http_adapter;
2 |
3 | pub use http_adapter::HttpAdapter;
4 |
--------------------------------------------------------------------------------
/mirai_rs/src/lib.rs:
--------------------------------------------------------------------------------
1 | mod adapter;
2 | pub mod message;
3 | pub mod mirai_http;
4 | pub mod model;
5 | pub mod response;
6 |
7 | pub use async_trait::async_trait;
8 | use core::panic;
9 | use message::{EventPacket, MessageEvent};
10 | use model::BaseResponse;
11 | use response::{AboutResponse, BindResponse, VerifyResponse};
12 | use serde_json::{json, Value};
13 | use std::collections::HashMap;
14 | use std::sync::Arc;
15 |
16 | pub type HttpResult = std::result::Result>;
17 |
18 | pub type Target = u64;
19 |
20 | pub struct Mirai {
21 | host: String,
22 | port: u32,
23 | verify_key: String,
24 | qq: u32,
25 | session_key: String,
26 | event_handler: Option>,
27 | }
28 |
29 | impl Mirai {
30 | pub fn builder(host: &str, port: u32, verify_key: &str) -> MiraiBuilder {
31 | MiraiBuilder {
32 | host: host.to_string(),
33 | port,
34 | verify_key: verify_key.to_string(),
35 | qq: 0,
36 | event_handler: None,
37 | }
38 | }
39 |
40 | /**
41 | * 认证
42 | * 发送verify_key获取session_key
43 | * https://github.com/project-mirai/mirai-api-http/blob/master/docs/adapter/HttpAdapter.md#%E8%AE%A4%E8%AF%81
44 | */
45 | pub async fn verify(&mut self) -> HttpResult {
46 | let js = json!({"verifyKey": self.verify_key});
47 | let client = reqwest::Client::new();
48 | let mut data = HashMap::new();
49 | data.insert("verifyKey", self.verify_key.as_str());
50 |
51 | let resp: VerifyResponse = client
52 | .post(self.get_url("/verify"))
53 | .json(&data)
54 | .send()
55 | .await?
56 | .json()
57 | .await?;
58 |
59 | self.session_key = resp.session.clone();
60 |
61 | Ok(resp)
62 | }
63 |
64 | pub async fn bind(&self) -> HttpResult {
65 | let js = json!({"verifyKey": self.verify_key});
66 | let client = reqwest::Client::new();
67 | let mut data: HashMap<&str, Value> = HashMap::new();
68 | data.insert("sessionKey", json!(self.session_key));
69 | data.insert("qq", json!(self.qq));
70 |
71 | println!("{:?}", data);
72 |
73 | let resp: BindResponse = client
74 | .post(self.get_url("/bind"))
75 | .json(&data)
76 | .send()
77 | .await?
78 | .json()
79 | .await?;
80 |
81 | Ok(resp)
82 | }
83 |
84 | pub async fn about() -> HttpResult {
85 | let client = reqwest::Client::new();
86 | let resp: AboutResponse = client
87 | .get("http://52.193.15.252:8080/about")
88 | .send()
89 | .await?
90 | .json()
91 | .await?;
92 |
93 | Ok(resp)
94 | }
95 |
96 | pub async fn fetch_message(&self, count: u32) -> HttpResult>> {
97 | let path = format!(
98 | "/fetchMessage?sessionKey={}&count={}",
99 | &self.session_key, count
100 | );
101 | let client = reqwest::Client::new();
102 | let resp: BaseResponse> = client
103 | .get(&self.get_url(path.as_str()))
104 | .send()
105 | .await?
106 | .json()
107 | .await?;
108 |
109 | Ok(resp)
110 | }
111 |
112 | pub async fn start(&mut self) {
113 | let event_handler = match &self.event_handler {
114 | Some(event_handler) => event_handler,
115 | None => {
116 | panic!("");
117 | }
118 | };
119 | loop {
120 | let result = self.fetch_message(1).await;
121 | let event_handler = event_handler.clone();
122 | match result {
123 | Ok(res) => {
124 | for item in res.data {
125 | if let EventPacket::MessageEvent(message) = item {
126 | event_handler.message(&self, message).await;
127 | continue;
128 | }
129 | println!("接收到其它消息");
130 | println!("{:?}", serde_json::to_string(&item).unwrap());
131 | }
132 | }
133 | Err(err) => {
134 | println!("{:?}", err);
135 | println!("获取信息失败");
136 | }
137 | }
138 | tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
139 | // let result = self.http_adapter.fetch_message(10).await;
140 | // println!("接收到消息? {:?}", result);
141 | // for event in result.data {
142 | // match event {
143 | // EventPacket::MessageEvent(message_event) => {
144 | // self.event_handler.message(message_event.clone()).await;
145 | // match message_event {
146 | // MessageEvent::GroupMessage(message) => {
147 | // self.event_handler.group_message(message).await
148 | // }
149 | // _ => println!(),
150 | // }
151 | // }
152 | // event => {
153 | // println!("{:?}", event);
154 | // eprintln!("没有处理的事件");
155 | // }
156 | // }
157 | // }
158 | }
159 | }
160 |
161 | pub async fn get_http(&self) -> mirai_http::MiraiHttp {
162 | mirai_http::MiraiHttp::new(self)
163 | }
164 |
165 | pub fn get_url(&self, uri: &str) -> String {
166 | return format!("http://{}:{}{}", self.host, self.port, uri);
167 | }
168 | }
169 |
170 | pub struct MiraiBuilder {
171 | host: String,
172 | port: u32,
173 | verify_key: String,
174 | qq: u32,
175 |
176 | event_handler: Option>,
177 | }
178 |
179 | impl MiraiBuilder {
180 | pub fn bind_qq(mut self, qq: u32) -> Self {
181 | self.qq = qq;
182 | self
183 | }
184 | /// Sets an event handler with multiple methods for each possible event.
185 | pub async fn event_handler(mut self, event_handler: H) -> Mirai {
186 | self.event_handler = Some(Arc::new(event_handler));
187 |
188 | let mut mirai = Mirai {
189 | host: self.host,
190 | port: self.port,
191 | verify_key: self.verify_key,
192 | qq: self.qq,
193 | event_handler: self.event_handler,
194 | session_key: "".to_string(),
195 | };
196 |
197 | println!("{},{}", &mirai.host, &mirai.port);
198 |
199 | match mirai.verify().await {
200 | Ok(res) => {
201 | mirai.session_key = res.session;
202 | }
203 | Err(err) => {
204 | eprintln!("{:?}", err);
205 | panic!("获取verify请求出错");
206 | }
207 | }
208 |
209 | match mirai.bind().await {
210 | Ok(res) => {}
211 | Err(err) => {
212 | println!("绑定qq请求出错")
213 | }
214 | }
215 |
216 | mirai
217 | }
218 | }
219 |
220 | pub mod api {
221 | pub use super::message::EventPacket;
222 | pub use super::message::MessageEvent;
223 | }
224 |
225 | /// The core trait for handling events by serenity.
226 | #[async_trait]
227 | pub trait EventHandler: Send + Sync {
228 | async fn message(&self, ctx: &Mirai, msg: MessageEvent);
229 | }
230 |
--------------------------------------------------------------------------------
/mirai_rs/src/message.rs:
--------------------------------------------------------------------------------
1 | use serde::Deserialize;
2 | use serde::Serialize;
3 | use serde_json::Value;
4 |
5 | use crate::model::group::{GroupMessage, GroupSender};
6 | use crate::Target;
7 |
8 | #[derive(Debug, Clone, Serialize, Deserialize)]
9 | #[serde(untagged)]
10 | pub enum EventPacket {
11 | MessageEvent(MessageEvent),
12 | // BotLoginEvent(),
13 | // BotMuteEvent(),
14 | // RecallEvent(),
15 | // GroupChangeEvent(),
16 | Unsupported(Value),
17 | }
18 |
19 | pub mod sender {
20 | use crate::Target;
21 | use serde::Deserialize;
22 | use serde::Serialize;
23 |
24 | #[derive(Debug, Clone, Serialize, Deserialize)]
25 | pub struct FriendSender {
26 | pub id: Target,
27 | pub nickname: String,
28 | pub remark: String,
29 | }
30 |
31 | #[derive(Debug, Clone, Serialize, Deserialize)]
32 | pub struct OtherClientSender {
33 | pub id: Target,
34 | pub platform: String,
35 | }
36 | }
37 |
38 | // #[serde(flatten)]
39 | // extra: std::collections::HashMap,
40 | #[derive(Debug, Clone, Serialize, Deserialize)]
41 | #[serde(tag = "type")]
42 | pub enum MessageEvent {
43 | GroupMessage(GroupMessage),
44 | TempMessage {
45 | #[serde(rename = "messageChain")]
46 | message_chain: MessageChain,
47 | sender: GroupSender,
48 | },
49 | FriendMessage {
50 | #[serde(rename = "messageChain")]
51 | message_chain: MessageChain,
52 | sender: sender::FriendSender,
53 | },
54 | StrangerMessage {
55 | #[serde(rename = "messageChain")]
56 | message_chain: MessageChain,
57 | sender: sender::FriendSender,
58 | },
59 | OtherClientMessage {
60 | #[serde(rename = "messageChain")]
61 | message_chain: MessageChain,
62 | sender: sender::OtherClientSender,
63 | },
64 | }
65 |
66 | pub type MessageChain = Vec;
67 |
68 | #[serde(tag = "type")]
69 | #[derive(Debug, Clone, Serialize, Deserialize)]
70 | pub enum MessageContent {
71 | Source {
72 | id: Target,
73 | time: u32,
74 | },
75 | #[serde(rename_all = "camelCase")]
76 | Quote {
77 | id: u32,
78 | group_id: Target,
79 | sender_id: Target,
80 | target_id: Target,
81 | origin: MessageChain,
82 | },
83 | At {
84 | target: Target,
85 | display: Option,
86 | },
87 | AtAll {},
88 | #[serde(rename_all = "camelCase")]
89 | Face {
90 | face_id: Option,
91 | name: Option,
92 | },
93 | Plain {
94 | text: String,
95 | },
96 | #[serde(rename_all = "camelCase")]
97 | Image {
98 | image_id: Option,
99 | url: Option,
100 | path: Option,
101 | base64: Option,
102 | },
103 | #[serde(rename_all = "camelCase")]
104 | FlashImage {
105 | image_id: Option,
106 | url: Option,
107 | path: Option,
108 | base64: Option,
109 | },
110 | #[serde(rename_all = "camelCase")]
111 | Voice {
112 | voice_id: Option,
113 | url: Option,
114 | path: Option,
115 | base64: Option,
116 | length: Option,
117 | },
118 | Xml {
119 | xml: String,
120 | },
121 | Json {
122 | json: String,
123 | },
124 | App {
125 | content: String,
126 | },
127 | Poke {
128 | name: Poke,
129 | },
130 | Dice {
131 | value: u16,
132 | },
133 | #[serde(rename_all = "camelCase")]
134 | MusicShare {
135 | kind: String,
136 | title: String,
137 | summary: String,
138 | jump_url: String,
139 | picture_url: String,
140 | music_url: String,
141 | brief: String,
142 | },
143 | ForwardMessage {
144 | sender_id: Target,
145 | time: u32,
146 | sender_name: String,
147 | message_chain: MessageChain,
148 | message_id: u16,
149 | },
150 | File {
151 | id: String,
152 | name: String,
153 | size: u32,
154 | },
155 | MiraiCode {
156 | code: String,
157 | },
158 | }
159 |
160 | #[derive(Debug, Clone, Serialize, Deserialize)]
161 | pub enum Poke {
162 | Poke,
163 | ShowLove,
164 | Like,
165 | Heartbroken,
166 | SixSixSix,
167 | FangDaZhao,
168 | }
169 |
--------------------------------------------------------------------------------
/mirai_rs/src/mirai_http.rs:
--------------------------------------------------------------------------------
1 | use crate::message::EventPacket;
2 | use crate::message::MessageChain;
3 | use crate::model::group::Member;
4 | use crate::model::BaseResponse;
5 | use crate::model::SendGroupMessageResponse;
6 | use crate::{HttpResult, Mirai};
7 |
8 | use serde_json::json;
9 | use std::collections::HashMap;
10 |
11 | pub struct MiraiHttp {
12 | host: String,
13 | port: u32,
14 | verify_key: String,
15 | qq: u32,
16 | session_key: String,
17 | req: reqwest::Client,
18 | }
19 |
20 | impl MiraiHttp {
21 | pub fn new(mirai: &Mirai) -> Self {
22 | MiraiHttp {
23 | host: mirai.host.clone(),
24 | port: mirai.port,
25 | verify_key: mirai.verify_key.clone(),
26 | qq: mirai.qq,
27 | session_key: mirai.session_key.clone(),
28 | req: reqwest::Client::new(),
29 | }
30 | }
31 |
32 | pub async fn fetch_message(&self, count: u32) -> HttpResult>> {
33 | let path = format!(
34 | "/fetchMessage?sessionKey={}&count={}",
35 | &self.session_key, count
36 | );
37 | let client = reqwest::Client::new();
38 | let resp: BaseResponse> = client
39 | .get(&self.get_url(path.as_str()))
40 | .send()
41 | .await?
42 | .json()
43 | .await?;
44 |
45 | Ok(resp)
46 | }
47 |
48 | pub async fn send_group_message(
49 | &self,
50 | message_chain: MessageChain,
51 | group: u64,
52 | ) -> HttpResult {
53 | let js = json!({
54 | "sessionKey": self.session_key,
55 | "group": group,
56 | "messageChain": message_chain
57 | });
58 | // let mut data = HashMap::new();
59 | // data.insert("verifyKey", self.verify_key.as_str());
60 |
61 | let response = match self
62 | .req
63 | .post(self.get_url("/sendGroupMessage"))
64 | .json(&js)
65 | .send()
66 | .await
67 | {
68 | Ok(resp) => resp,
69 | Err(err) => {
70 | println!("[mirai_http] send_group_message请求失败");
71 | println!("[mirai_http] {:?}", err);
72 | Result::Err(err)?
73 | }
74 | };
75 | println!("[mirai_http] send_group_message {}", response.status());
76 | let resp = response.text().await.unwrap();
77 | let resp: SendGroupMessageResponse = match serde_json::from_str(resp.as_str()) {
78 | Ok(resp) => resp,
79 | Err(err) => {
80 | println!("[mirai_http] send_group_message转换json失败");
81 | println!("[mirai_http] {:?}", resp);
82 | println!("[mirai_http] {:?}", err);
83 | Result::Err(err)?
84 | }
85 | };
86 |
87 | Ok(resp)
88 | }
89 |
90 | pub async fn member_list(&self, target: u64) -> HttpResult>> {
91 | let path = format!(
92 | "/memberList?sessionKey={}&target={}",
93 | &self.session_key, target
94 | );
95 |
96 | let response = match self.req.get(self.get_url(path.as_str())).send().await {
97 | Ok(resp) => resp,
98 | Err(err) => {
99 | println!("[mirai_http] member_list请求失败");
100 | println!("[mirai_http] {:?}", err);
101 | Result::Err(err)?
102 | }
103 | };
104 | println!("[mirai_http] member_list {}", response.status());
105 | let resp = response.text().await.unwrap();
106 | let resp: BaseResponse> = match serde_json::from_str(resp.as_str()) {
107 | Ok(resp) => resp,
108 | Err(err) => {
109 | println!("[mirai_http] member_list转换json失败");
110 | println!("[mirai_http] {:?}", resp);
111 | println!("[mirai_http] {:?}", err);
112 | Result::Err(err)?
113 | }
114 | };
115 |
116 | Ok(resp)
117 | }
118 |
119 | pub async fn get_member_info(&self, target: u64, member_id: u64) -> HttpResult {
120 | let path = format!(
121 | "/memberInfo?sessionKey={}&target={}&memberId={}",
122 | &self.session_key, target, member_id
123 | );
124 |
125 | let response = match self.req.get(self.get_url(path.as_str())).send().await {
126 | Ok(resp) => resp,
127 | Err(err) => {
128 | println!("[mirai_http] get_member_info请求失败");
129 | println!("[mirai_http] {:?}", err);
130 | Result::Err(err)?
131 | }
132 | };
133 | println!("[mirai_http] get_member_info {}", response.status());
134 | let resp = response.text().await.unwrap();
135 | let resp: Member = match serde_json::from_str(resp.as_str()) {
136 | Ok(resp) => resp,
137 | Err(err) => {
138 | println!("[mirai_http] get_member_info转换json失败");
139 | println!("[mirai_http] {:?}", resp);
140 | println!("[mirai_http] {:?}", err);
141 | Result::Err(err)?
142 | }
143 | };
144 |
145 | Ok(resp)
146 | }
147 |
148 | pub fn get_url(&self, uri: &str) -> String {
149 | return format!("http://{}:{}{}", self.host, self.port, uri);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/mirai_rs/src/model/group.rs:
--------------------------------------------------------------------------------
1 | use crate::message::MessageChain;
2 | use crate::Target;
3 | use serde::Deserialize;
4 | use serde::Serialize;
5 | use serde_json::Value;
6 |
7 | #[derive(Debug, Clone, Serialize, Deserialize)]
8 | pub struct GroupMessage {
9 | #[serde(rename = "messageChain")]
10 | pub message_chain: MessageChain,
11 | pub sender: GroupSender,
12 | }
13 |
14 | /**
15 | *
16 | */
17 | #[derive(Debug, Clone, Serialize, Deserialize)]
18 | pub enum Permission {
19 | #[serde(rename = "ADMINISTRATOR")]
20 | Administrator,
21 |
22 | #[serde(rename = "OWNER")]
23 | Owner,
24 |
25 | #[serde(rename = "MEMBER")]
26 | Member,
27 | }
28 |
29 | #[derive(Debug, Clone, Serialize, Deserialize)]
30 | pub struct GroupSender {
31 | pub id: Target,
32 |
33 | #[serde(rename = "memberName")]
34 | pub member_name: String,
35 |
36 | #[serde(rename = "specialTitle")]
37 | pub special_title: String,
38 |
39 | pub permission: Permission,
40 |
41 | #[serde(rename = "joinTimestamp")]
42 | pub join_timestamp: u64,
43 |
44 | #[serde(rename = "lastSpeakTimestamp")]
45 | pub last_speak_timestamp: u64,
46 |
47 | #[serde(rename = "muteTimeRemaining")]
48 | pub mute_time_remaining: u64,
49 |
50 | pub group: Group,
51 | }
52 |
53 | #[derive(Debug, Clone, Serialize, Deserialize)]
54 | pub struct Group {
55 | pub id: Target,
56 | pub name: String,
57 | pub permission: Permission,
58 | }
59 | /**
60 | * 群成员信息类型
61 | */
62 | #[derive(Debug, Clone, Serialize, Deserialize)]
63 | pub struct Member {
64 | /**
65 | * 群名片
66 | */
67 | #[serde(rename = "memberName")]
68 | pub member_name: String,
69 |
70 | /**
71 | * 群权限 OWNER、ADMINISTRATOR 或 MEMBER
72 | */
73 | permission: Permission,
74 | /**
75 | * 群头衔
76 | */
77 | #[serde(rename = "specialTitle")]
78 | special_title: String,
79 | /**
80 | * 入群时间戳
81 | */
82 | #[serde(rename = "joinTimestamp")]
83 | join_timestamp: i64,
84 | /**
85 | * 上一次发言时间戳
86 | */
87 | #[serde(rename = "lastSpeakTimestamp")]
88 | last_speak_timestamp: i64,
89 | /**
90 | * 剩余禁言时间
91 | */
92 | #[serde(rename = "muteTimeRemaining")]
93 | mute_time_remaining: u64,
94 | /**
95 | * 所在的群
96 | */
97 | group: Group,
98 | }
99 |
--------------------------------------------------------------------------------
/mirai_rs/src/model/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod group;
2 |
3 | use serde::Deserialize;
4 | use serde::Serialize;
5 | use serde_json::Value;
6 |
7 | #[derive(Debug, Serialize, Deserialize)]
8 | pub struct SendGroupMessageResponse {
9 | pub code: u32,
10 | pub msg: String,
11 | pub messageId: u64,
12 | }
13 |
14 | /**
15 | * 基础响应格式
16 | */
17 | #[derive(Debug, Serialize, Deserialize)]
18 | pub struct BaseResponse {
19 | pub code: u32,
20 | pub msg: String,
21 | pub data: T,
22 | }
23 |
--------------------------------------------------------------------------------
/mirai_rs/src/response.rs:
--------------------------------------------------------------------------------
1 | use serde::Deserialize;
2 | use serde::Serialize;
3 |
4 | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
5 | pub struct AboutResponse {
6 | pub code: u32,
7 | pub data: AboutData,
8 | }
9 |
10 | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
11 | pub struct AboutData {
12 | pub version: String,
13 | }
14 |
15 | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
16 | pub struct VerifyResponse {
17 | pub code: u32,
18 | pub session: String,
19 | }
20 |
21 | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
22 | pub struct BindResponse {
23 | pub code: u32,
24 | pub msg: String,
25 | }
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "parse-server",
3 | "version": "3.0.0",
4 | "description": "",
5 | "main": "server.js",
6 | "scripts": {
7 | "build": "tsc server.ts",
8 | "start": "ts-node --project tsconfig.json server.ts "
9 | },
10 | "dependencies": {
11 | "discord-markdown": "^2.5.0"
12 | },
13 | "devDependencies": {
14 | "@types/express": "^4.17.11",
15 | "@types/node": "^14.14.31",
16 | "ts-loader": "^8.0.17",
17 | "ts-node": "^10.9.1",
18 | "typescript": "^4.1.5"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | max_width = 140
--------------------------------------------------------------------------------
/server.ts:
--------------------------------------------------------------------------------
1 | import * as http from 'http';
2 | import { markdownEngine as markdown, htmlTag } from 'discord-markdown';
3 |
4 | const server = http.createServer(async (req, res) => {
5 | console.log('ok');
6 |
7 | const url = req.url?.split('?')[0];
8 | const bodyStr = await new Promise((res, rej) => {
9 | let bodyStr = '';
10 | req.on('data', (chunk) => {
11 | bodyStr += chunk
12 | })
13 | req.on('end', () => {
14 | res(bodyStr);
15 | })
16 | })
17 | console.log(bodyStr);
18 | if (url === '/parse-discord-markdown') {
19 | const data = parseDiscrodMarkdown(bodyStr);
20 | console.log(data);
21 | res.setHeader('Content-Type', 'application/json')
22 | res.end(JSON.stringify(data));
23 | return;
24 | }
25 | })
26 |
27 | function parseDiscrodMarkdown(message: string): any {
28 | const ast = markdown.parserFor({
29 | atDC: bridgeRule.atDC,
30 | atQQ: bridgeRule.atQQ,
31 | atKHL: bridgeRule.atKHL,
32 | DiscordAtUser: bridgeRule.DiscordAtUser,
33 | DiscordAtEveryone: bridgeRule.DiscordAtEveryone,
34 | DiscordAtHere: bridgeRule.DiscordAtHere,
35 | DiscordEmoji: bridgeRule.DiscordEmoji,
36 | Plain: bridgeRule.Plain,
37 | })(message) as Array<{ type: 'DiscordAtUser' | 'DiscordEmoji', [prop: string]: any }>;
38 | return ast;
39 | }
40 |
41 | export const bridgeRule = {
42 | atDC: {
43 | order: 0,
44 | match: source => /^@\[DC\] [^\n]+?#\d\d\d\d/.exec(source),
45 | parse: function (capture, parse, state) {
46 | console.log(capture);
47 | return { type: 'At', username: capture[0] };
48 | },
49 | html: function (node, output, state) {
50 | return '{{atDc}}';
51 | },
52 | },
53 | atKHL: {
54 | order: 0,
55 | match: source => /^@\[KHL\] ([^\n#]+)#(\d\d\d\d)/.exec(source),
56 | parse: function (capture, parse, state) {
57 | console.log(capture);
58 | return { type: 'At', source: 'KHL', username: capture[1], discriminator: capture[2] };
59 | },
60 | html: function (node, output, state) {
61 | return '{{atDc}}';
62 | },
63 | },
64 | atQQ: {
65 | order: 0,
66 | match: source => /^@\[QQ\] [^\n]+?\([0-9]+\)/.exec(source),
67 | parse: function (capture, parse, state) {
68 | console.log(capture);
69 | return { type: 'At', username: capture[0] };
70 | },
71 | html: function (node, output, state) {
72 | return '{{atDc}}';
73 | },
74 | },
75 | Plain: Object.assign({}, markdown.defaultRules.text, {
76 | match: source => /^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff-]|\n\n|\n|\w+:\S|$)/.exec(source),
77 | parse: function (capture, parse, state) {
78 | return { type: 'Plain', text: capture[0] };
79 | },
80 | html: function (node, output, state) {
81 | if (state.escapeHTML)
82 | return markdown.sanitizeText(node.content);
83 |
84 | return node.content;
85 | },
86 | }),
87 | DiscordAtUser: {
88 | order: markdown.defaultRules.strong.order,
89 | match: source => /^<@!?([0-9]*)>/.exec(source),
90 | parse: function (capture) {
91 | return {
92 | id: capture[1]
93 | };
94 | },
95 | html: function (node, output, state) {
96 | return htmlTag('span', state.discordCallback.user(node), { class: 'd-mention d-user' }, state);
97 | }
98 | },
99 | DiscordAtEveryone: {
100 | order: markdown.defaultRules.strong.order,
101 | match: source => /^@everyone/.exec(source),
102 | parse: function () {
103 | return { };
104 | },
105 | html: function (node, output, state) {
106 | return htmlTag('span', state.discordCallback.everyone(node), { class: 'd-mention d-user' }, state);
107 | },
108 | },
109 | DiscordAtHere: {
110 | order: markdown.defaultRules.strong.order,
111 | match: source => /^@here/.exec(source),
112 | parse: function () {
113 | return { };
114 | },
115 | html: function (node, output, state) {
116 | return htmlTag('span', state.discordCallback.here(node), { class: 'd-mention d-user' }, state);
117 | }
118 | },
119 | DiscordEmoji: {
120 | order: markdown.defaultRules.strong.order,
121 | match: source => /^<(a?):(\w+):(\d+)>/.exec(source),
122 | parse: function (capture) {
123 | return {
124 | animated: capture[1] === "a",
125 | name: capture[2],
126 | id: capture[3],
127 | };
128 | },
129 | html: function (node, output, state) {
130 | return htmlTag('img', '', {
131 | class: `d-emoji${node.animated ? ' d-emoji-animated' : ''}`,
132 | src: `https://cdn.discordapp.com/emojis/${node.id}.${node.animated ? 'gif' : 'png'}`,
133 | alt: `:${node.name}:`
134 | }, false, state);
135 | }
136 | },
137 | khlEveryone: {
138 | order: markdown.defaultRules.strong.order,
139 | match: source => /\(met\)all\(met\)/.exec(source),
140 | parse: function () {
141 | return { type: 'AtAll' };
142 | },
143 | html: function (node, output, state) {
144 | return htmlTag('span', state.discordCallback.everyone(node), { class: 'd-mention d-user' }, state);
145 | },
146 | },
147 | };
148 |
149 | parseDiscrodMarkdown(`@[DC] 6uopdong#4700
150 | !绑定 qq 1261972160 asd`);
151 | server.listen(3000)
152 |
--------------------------------------------------------------------------------
/src/bridge/bridge_message.rs:
--------------------------------------------------------------------------------
1 | use crate::config::BridgeConfig;
2 | use serde::Deserialize;
3 | use serde::Serialize;
4 |
5 | #[derive(Debug, Clone, Serialize, Deserialize)]
6 | pub struct BridgeMessage {
7 | pub id: String,
8 | // 桥用户
9 | pub sender_id: String,
10 | // 头像链接
11 | pub avatar_url: Option,
12 | pub bridge_config: BridgeConfig,
13 | pub message_chain: MessageChain,
14 | }
15 |
16 | pub type MessageChain = Vec;
17 |
18 | #[derive(Debug, Clone, Serialize, Deserialize)]
19 | #[serde(tag = "type")]
20 | pub enum MessageContent {
21 | /**
22 | * 回复
23 | */
24 | Reply {
25 | /**
26 | * 想要回复的桥消息id
27 | */
28 | id: Option,
29 | },
30 | /**
31 | * 普通文本
32 | */
33 | Plain {
34 | text: String,
35 | },
36 | /**
37 | * 提及某人
38 | */
39 | At {
40 | /**
41 | * 目标用户的桥用户id
42 | */
43 | id: String,
44 | },
45 | /**
46 | * 提及所有人
47 | */
48 | AtAll,
49 | /**
50 | * 图片
51 | */
52 | Image(Image),
53 | /**
54 | * 发生了一些错误
55 | */
56 | Err {
57 | // 错误信息
58 | message: String,
59 | },
60 | Othen,
61 | }
62 |
63 | #[derive(Debug, Clone, Serialize, Deserialize)]
64 | pub enum Image {
65 | Url(String),
66 | Path(String),
67 | Buff(Vec),
68 | }
69 |
70 | impl Image {
71 | pub(crate) async fn load_data(self) -> anyhow::Result> {
72 | match self {
73 | Image::Url(url) => Ok(reqwest::get(url).await?.bytes().await?.to_vec()),
74 | Image::Path(path) => Ok(tokio::fs::read(path).await?),
75 | Image::Buff(data) => Ok(data),
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/bridge/config.rs:
--------------------------------------------------------------------------------
1 | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
2 | pub struct Config {
3 | /// 是否将二维码打印到终端
4 | #[serde(rename = "printQR")]
5 | pub print_qr: Option,
6 | #[serde(rename = "qqConfig")]
7 | pub qq_config: QQConfig,
8 | #[serde(rename = "discordConfig")]
9 | pub discord_config: DiscordConfig,
10 | #[serde(rename = "telegramConfig")]
11 | pub telegram_config: TelegramConfig,
12 | pub bridges: Vec,
13 | }
14 |
15 | impl Config {
16 | pub fn new() -> Self {
17 | let file = fs::read_to_string("./config.json").unwrap();
18 | // println!("{file}");
19 | let config: Config = serde_json::from_str(file.as_str()).unwrap();
20 |
21 | config
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/bridge/manager/message_manager.rs:
--------------------------------------------------------------------------------
1 | use lazy_static::lazy_static;
2 | use std::fs;
3 | use tokio::sync::Mutex;
4 |
5 | use crate::bridge;
6 | use bridge::pojo::{BridgeMessagePO, BridgeMessageRefMessageForm};
7 | pub struct BridgeMessageManager {
8 | messages: Vec,
9 | }
10 |
11 | impl BridgeMessageManager {
12 | pub fn new() -> BridgeMessageManager {
13 | let path = "./data/bridge_message.json";
14 | if let Ok(true) = fs::try_exists(path) {
15 | let file = fs::read_to_string(path).unwrap();
16 | return BridgeMessageManager {
17 | messages: serde_json::from_str(file.as_str()).unwrap(),
18 | };
19 | }
20 | BridgeMessageManager { messages: vec![] }
21 | }
22 |
23 | /**
24 | * 查询指定消息
25 | */
26 | pub async fn get(&self, id: &str) -> Option {
27 | for message in &self.messages {
28 | if id.eq(&message.id) {
29 | return Some(message.clone());
30 | }
31 | }
32 | None
33 | }
34 | /**
35 | * 保存消息
36 | */
37 | pub async fn save(&mut self, form: bridge::pojo::BridgeSendMessageForm) -> String {
38 | let id = uuid::Uuid::new_v4().to_string();
39 | let mut bridge_message = bridge::pojo::BridgeMessagePO {
40 | id: id.clone(),
41 | refs: vec![],
42 | sender_id: form.sender_id,
43 | avatar_url: form.avatar_url,
44 | message_chain: form.message_chain,
45 | };
46 | bridge_message.refs.push(form.origin_message);
47 | self.messages.push(bridge_message);
48 | self.serialize();
49 | id
50 | }
51 |
52 | /**
53 | * 关联消息桥消息
54 | */
55 | pub async fn ref_bridge_message(&mut self, form: BridgeMessageRefMessageForm) -> bool {
56 | let message = self
57 | .messages
58 | .iter_mut()
59 | .find(|message| form.bridge_message_id.eq(&message.id));
60 | match message {
61 | Some(message) => {
62 | message.refs.push(bridge::pojo::BridgeMessageRefPO {
63 | origin_id: form.origin_id,
64 | platform: form.platform,
65 | });
66 | self.serialize();
67 | true
68 | }
69 | None => false,
70 | }
71 | // for user in &self.bridge_users {
72 | // if origin_id.eq(&user.origin_id) && platform.eq(&user.platform) {
73 | // return Some(user.clone());
74 | // }
75 | // }
76 | // let mut bridge_message = bridge::pojo::BridgeMessagePO {
77 | // id: id.clone(),
78 | // refs: vec![],
79 | // };
80 | // bridge_message.refs.push(bridge::pojo::BridgeMessageRefPO {
81 | // platform: form.platform,
82 | // origin_id: form.origin_id,
83 | // });
84 | // self.messages.push(bridge_message);
85 | // self.serialize();
86 | // id
87 | }
88 |
89 | /**
90 | * 根据关联id和平台查询桥消息
91 | */
92 | pub async fn find_by_ref_and_platform(
93 | &self,
94 | origin_id: &str,
95 | platform: &str,
96 | ) -> Result