├── .github ├── ISSUE_TEMPLATE │ ├── bug.yml │ ├── config.yml │ ├── group.yml │ ├── new.yml │ └── optimize.yml ├── reviewer-lottery.yml └── workflows │ ├── assign-issues.yml │ ├── auto-merge.yml │ ├── build-and-push-docker-image.yml │ ├── deloy.yml │ ├── label-issue.yml │ ├── label-issues.yml │ ├── reviewer-lottery.yml │ └── welcome-issue.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── LICENSE ├── README.md ├── README_en.md ├── bot.py ├── docker-compose.yml ├── docs ├── .vitepress │ └── config.js ├── games │ └── ftt.md ├── index.md ├── invite.md └── public │ ├── CNAME │ ├── XDbot2.png │ ├── bricks.png │ ├── cobweb.png │ ├── diamond_block.png │ ├── grass_block_top.png │ ├── iron_block.png │ ├── piston_top.png │ ├── portal.png │ ├── sand.png │ └── stone_bricks.png ├── man ├── README.md ├── autosell │ ├── 0.md │ └── 1.md ├── bag │ └── 0.md ├── bank │ └── 0.md ├── calc │ └── 0.md ├── cave │ ├── 0.md │ ├── 1.md │ └── 2.md ├── checkout │ └── 0.md ├── cm │ └── 0.md ├── ct │ └── 0.md ├── fakenode │ └── 0.md ├── function │ └── 0.md ├── glossary │ └── 0.md ├── gpt │ └── 0.md ├── help │ └── 0.md ├── items │ ├── 0.md │ └── 1.md ├── jrrp │ ├── 0.md │ └── 1.md ├── linuxman │ └── 0.md ├── man │ └── 0.md ├── market │ └── 0.md ├── mcver │ └── 0.md ├── node │ ├── 0.md │ └── 1.md ├── pacman │ └── 0.md ├── preview │ └── 0.md ├── quick-math │ └── 0.md ├── reply │ └── 0.md ├── rules │ └── 0.md ├── shop │ └── 0.md ├── sign │ └── 0.md ├── status │ └── 0.md ├── su │ ├── 0.md │ ├── 1.md │ ├── 10.md │ ├── 11.md │ ├── 12.md │ ├── 13.md │ ├── 14.md │ ├── 15.md │ ├── 16.md │ ├── 17.md │ ├── 18.md │ ├── 2.md │ ├── 25.md │ ├── 26.md │ ├── 27.md │ ├── 28.md │ ├── 3.md │ ├── 4.md │ ├── 5.md │ ├── 6.md │ ├── 7.md │ ├── 8.md │ └── 9.md ├── synthesis │ └── 0.md ├── use │ └── 0.md └── vote │ └── 0.md ├── package.json ├── pyproject.toml ├── requirements.txt └── src └── plugins ├── Core ├── __init__.py ├── chatgpt_template │ ├── default.txt │ └── empty.txt ├── config.py ├── disabled_plugins │ ├── _autoreply.py │ ├── _bots.py │ ├── _chatgpt.py │ ├── _fakenode.py │ ├── _help.py │ ├── _qm_avg.py │ ├── _quick_math.py │ ├── _st.py │ ├── _su_reply.py │ ├── _userCtrl.py │ ├── duel.py │ ├── duel │ │ ├── base_properties │ │ │ └── primary.json │ │ ├── buff │ │ │ ├── action_again.json │ │ │ ├── burn.json │ │ │ ├── freezing.json │ │ │ ├── reduce_speed_010.json │ │ │ └── shield.json │ │ ├── duel │ │ │ ├── __init__.py │ │ │ ├── contingent.py │ │ │ ├── controller.py │ │ │ ├── monomer.py │ │ │ ├── path.py │ │ │ ├── round_boundaries.py │ │ │ └── scheduler.py │ │ ├── effect_functions.txt │ │ └── kits │ │ │ ├── ice_king.json │ │ │ ├── leather_case.json │ │ │ ├── scrorching_sun_phantom.json │ │ │ ├── thunder_fury.json │ │ │ └── whirlwind_dancer.json │ ├── duel_command.py │ ├── duel_equip.py │ ├── essencemsg.py │ ├── genreator.py │ ├── test_duel.py │ ├── upload_logger.py │ └── wish.py ├── ehm │ ├── _unknown_error.json │ ├── github_not_login.json │ ├── item_not_found.json │ ├── openai_connection_error.json │ └── template.md ├── font │ └── HYRunYuan-55W.ttf ├── getHelp.py ├── get_version.py ├── lang │ ├── en_fzz.json │ ├── en_laman28.json │ ├── en_laman28_reverse.json │ ├── en_laman28_sha256.json │ ├── zh_bronya.json │ ├── zh_cat.json │ ├── zh_fzz.json │ ├── zh_hans.json │ ├── zh_hant_fzz.json │ └── zh_hk.json ├── lib │ ├── FindingTheTrail │ │ ├── LICENSE │ │ ├── argv.py │ │ ├── const.py │ │ ├── image.py │ │ ├── images │ │ │ ├── bricks.png │ │ │ ├── cobweb.png │ │ │ ├── diamond_block.png │ │ │ ├── grass_block_top.png │ │ │ ├── iron_block.png │ │ │ ├── piston_top.png │ │ │ ├── portal.png │ │ │ ├── sand.png │ │ │ └── stone_bricks.png │ │ ├── main.py │ │ ├── map.py │ │ ├── noise.py │ │ └── search.py │ └── md2img │ │ └── markdown2image │ │ ├── __init__.py │ │ ├── css_not_passed.json │ │ ├── default_style │ │ └── style.json │ │ ├── font │ │ └── HYRunYuan-55W.ttf │ │ ├── parser.py │ │ └── style.py ├── plugins │ ├── _error.py │ ├── _lang.py │ ├── _messenger.py │ ├── _returning_gift.py │ ├── _sign.py │ ├── _smart_reply.py │ ├── _utils.py │ ├── account.py │ ├── achievement.py │ ├── auto_approve.py │ ├── bag.py │ ├── bag_crr.py │ ├── bag_overflow.py │ ├── bag_tidy.py │ ├── ban.py │ ├── bank.py │ ├── bots.py │ ├── calc.py │ ├── cave.py │ ├── cave_comment_command.py │ ├── chatgptv2.py │ ├── code.py │ ├── command_manager.py │ ├── create_manpage.py │ ├── ct.py │ ├── duel.py │ ├── duel_items.py │ ├── email.py │ ├── etm │ │ ├── achievement.json │ │ ├── achievement.py │ │ ├── bag │ │ │ ├── __init__.py │ │ │ ├── crr.py │ │ │ └── overflow.py │ │ ├── buff.py │ │ ├── buffs.json │ │ ├── data.py │ │ ├── duel │ │ │ ├── buff │ │ │ │ ├── CriticalStrikeChanceIncreased.py │ │ │ │ ├── CriticalStrikeHarmIncreased.py │ │ │ │ ├── __init__.py │ │ │ │ ├── focus_down.py │ │ │ │ └── harm_up.py │ │ │ ├── entity │ │ │ │ ├── __init__.py │ │ │ │ └── user.py │ │ │ ├── item │ │ │ │ ├── __init__.py │ │ │ │ ├── consumables.py │ │ │ │ ├── hand.py │ │ │ │ ├── passive.py │ │ │ │ ├── relic.py │ │ │ │ └── weapons.py │ │ │ ├── logger.py │ │ │ └── scheduler.py │ │ ├── economy.py │ │ ├── exception.py │ │ ├── exp.py │ │ ├── health.py │ │ ├── item.py │ │ ├── item_basic_data.py │ │ ├── item_list.py │ │ ├── items │ │ │ ├── archfiend_dice.py │ │ │ ├── auto_sign_coupon.py │ │ │ ├── book_and_quill.py │ │ │ ├── dice.py │ │ │ ├── duel │ │ │ │ ├── consumables │ │ │ │ │ └── firecracker.py │ │ │ │ └── passive │ │ │ │ │ └── magic_cat.py │ │ │ ├── experience.py │ │ │ ├── gpt_monthly_pass.py │ │ │ ├── mystery_box.py │ │ │ ├── pawcoin.py │ │ │ ├── pouch.py │ │ │ ├── rubbish.py │ │ │ ├── stick.py │ │ │ ├── talisman.py │ │ │ ├── towel_zip.py │ │ │ ├── unknown_item.py │ │ │ └── vimcoin.py │ │ ├── json2items.py │ │ ├── merger.py │ │ ├── nbt.py │ │ ├── registry │ │ │ ├── registries.py │ │ │ └── registry.py │ │ └── user.py │ ├── extra.py │ ├── forward.py │ ├── ftt.py │ ├── ghot.py │ ├── github.py │ ├── gpt_config.py │ ├── gpt_scale.py │ ├── gptcommandhelp.py │ ├── groupwelcome.py │ ├── gssearch.py │ ├── help.py │ ├── jrrp.py │ ├── lang.py │ ├── latex.py │ ├── lgs.py │ ├── linuxkernelnews.py │ ├── linuxman.py │ ├── macount.py │ ├── man.py │ ├── market.py │ ├── mcver.py │ ├── md2img.py │ ├── messenger.py │ ├── node_manager.py │ ├── pacman.py │ ├── pawcoin.py │ ├── pay.py │ ├── poke.py │ ├── preview.py │ ├── progress.py │ ├── quick_calculus.py │ ├── quickmath.py │ ├── quickmath_points.py │ ├── random_number.py │ ├── redpacket.py │ ├── reply.py │ ├── reply_probability.py │ ├── report.py │ ├── search.py │ ├── send_email.py │ ├── setu.py │ ├── setu_rank.py │ ├── shop.py │ ├── sign.py │ ├── sixcount.py │ ├── smart_reply.py │ ├── statistics.py │ ├── status.py │ ├── su.py │ ├── su_ban.py │ ├── su_call.py │ ├── su_cave.py │ ├── su_config.py │ ├── su_ct.py │ ├── su_echo.py │ ├── su_forward.py │ ├── su_give.py │ ├── su_image.py │ ├── su_log.py │ ├── su_notice.py │ ├── su_old_reply.py │ ├── su_plugin.py │ ├── su_reset.py │ ├── su_restart.py │ ├── su_screenshot.py │ ├── su_update.py │ ├── su_update_notice.py │ ├── sudo.py │ ├── synthesis.py │ ├── unread_email_reminder.py │ ├── use.py │ ├── userinfo.py │ ├── version.py │ ├── vote.py │ ├── whoAtme.py │ └── whoami.py └── synthesis │ └── mysterybox_lv1.json └── nonebot_plugin_picmcstat ├── LICENSE ├── README.md ├── __init__.py ├── __main__.py ├── config.py ├── const.py ├── draw.py ├── res.py ├── res ├── default.png ├── dirt.png └── grass_side_carried.png └── util.py /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: "Bug 反馈" 2 | description: "发现了 XDbot 的 Bug" 3 | title: "[Bug 反馈] " 4 | labels: [· Bug] 5 | body: 6 | - type: textarea 7 | id: "yml-2" 8 | attributes: 9 | label: 描述 10 | description: "详细描述该 Bug 的具体表现。" 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: "yml-3" 15 | attributes: 16 | label: 重现步骤 17 | description: "详细描述要怎么操作才能再次触发这个 Bug。" 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: "yml-4" 22 | attributes: 23 | label: 截图 24 | description: "上传 Bug 触发时的截图" 25 | placeholder: "先点击这个文本框,然后再将文件直接拖拽到文本框中以上传,或者复制图片,在文本框内粘贴。" 26 | validations: 27 | required: false 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 提问 4 | url: https://jq.qq.com/?_wv=1027&k=eGgYHrRK 5 | about: 我不想提交反馈,只是想问一些问题 (进入XDbot用户群) 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/group.yml: -------------------------------------------------------------------------------- 1 | name: 官服节点加入新群 2 | description: 让官服机器人加入一个新群 3 | title: "[加入新群] " 4 | labels: ["加群申请"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | 感谢您使用XDbot2。若您需要让XDbot2加入您的QQ群,请填写下方的表单。申请提交后,我们将会在24小时内进行处理。 10 | 11 | 另请注意,我们可能会要求您提交更多信息。因此,请关注您的GitHub绑定邮箱通知。若您未在24小时内回复我们的要求,我们将关闭您的申请,不过您还可以再次提交。 12 | - type: checkboxes 13 | id: preflight 14 | attributes: 15 | label: 开始之前…… 16 | description: 让我们看看你的登机牌准备好了吗 17 | options: 18 | - label: 我是群组的管理员,或群主或管理员允许机器人入群 19 | required: true 20 | - type: markdown 21 | attributes: 22 | value: | 23 | 我们知道您不想阅读冗长的服务条款,但仍请您特别注意: 24 | - 您不得在使用本服务时利用提供的功能或漏洞,直接或间接地诱导XDbot2发送违反法律法规、欺诈、虚假或产生误导的信息。 25 | - 无论XDbot2是否处于运行状态,都不得对XDbot2进行言论攻击、滥用服务以及无故禁言、踢出等不友善行为。 26 | - 我们会根据实际情况自行裁定不友善行为,并不限于此处列出的类型。 27 | - 不得利用漏洞或使用相关工具对服务器进行攻击,如发现应及时向我们报告。 28 | - 您不得规避本服务的任何访问或可用性限制。不得侵犯他人的权利。 29 | - 您不得帮助他人违反这些规则。 30 | 31 | 对于任何违反上述协议、或是由我们判定为出现其它不合适的行为,我们将视情况终止对您的服务,并有权利公开相关的不良行为。 32 | - type: input 33 | id: qq-id 34 | attributes: 35 | label: QQ号 36 | description: 您的QQ号。 37 | placeholder: e.g. 10000 38 | validations: 39 | required: true 40 | - type: input 41 | id: group-id 42 | attributes: 43 | label: QQ群 44 | description: 您想要让机器人加入的群的群号。 45 | placeholder: e.g. 1000000 46 | validations: 47 | required: true 48 | - type: textarea 49 | id: group-key 50 | attributes: 51 | label: 入群方式 52 | description: 群组是否有特殊的入群答案?是否只支持二维码加群?请将它们备注至此。 53 | value: 本群无特殊入群答案,支持搜索群号加群,无需二维码或入群链接。 54 | validations: 55 | required: true 56 | - type: textarea 57 | id: admin-consent 58 | attributes: 59 | label: 群主或管理员同意 60 | description: 若您不是群组的群主/管理员,请附一张群主/管理员允许机器人入群的截图。 61 | value: 我是本群的群主或管理员。 62 | validations: 63 | required: true 64 | - type: textarea 65 | id: other 66 | attributes: 67 | label: 备注 68 | description: 你还想告诉我们什么? 69 | placeholder: 什么都可以哦,没有就算了吧。 70 | validations: 71 | required: false 72 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new.yml: -------------------------------------------------------------------------------- 1 | name: "新功能提案" 2 | description: "对已有功能的大幅度修改,或添加一个新功能" 3 | title: "[新功能提案] " 4 | labels: [· 新功能] 5 | body: 6 | - type: textarea 7 | id: "yml-2" 8 | attributes: 9 | label: 描述 10 | description: "详细描述你想添加的功能具体是怎样的。" 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: "yml-3" 15 | attributes: 16 | label: 原因 17 | description: "描述你为什么需要这项功能" 18 | validations: 19 | required: false 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/optimize.yml: -------------------------------------------------------------------------------- 1 | name: "优化建议" 2 | description: "对已有功能的小幅度优化或改进建议" 3 | title: "[优化建议] " 4 | labels: [· 优化] 5 | body: 6 | - type: textarea 7 | id: "yml-2" 8 | attributes: 9 | label: 描述 10 | description: "详细描述具体需要优化哪些地方,要改成怎样的。" 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: "yml-3" 15 | attributes: 16 | label: 原因 17 | description: "描述这项优化有什么好处" 18 | validations: 19 | required: false 20 | -------------------------------------------------------------------------------- /.github/reviewer-lottery.yml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: xxtg666 # name of the group 3 | reviewers: 3 # how many reviewers do you want to assign? 4 | internal_reviewers: 2 # how many reviewers do you want to assign when the PR author belongs to this group? 5 | usernames: # github usernames of the reviewers 6 | - xxtg666 7 | - This-is-XiaoDeng 8 | - chun-awa 9 | -------------------------------------------------------------------------------- /.github/workflows/assign-issues.yml: -------------------------------------------------------------------------------- 1 | name: 'Auto Assign (for issues)' 2 | on: 3 | issues: 4 | types: [opened] 5 | jobs: 6 | run: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - name: 'Auto-assign issue' 13 | uses: pozil/auto-assign-issue@v1 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | assignees: This-is-XiaoDeng 17 | numOfAssignee: 1 18 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: "Auto Merge PR" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - ready_for_review 8 | 9 | jobs: 10 | auto_merge: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v2 16 | 17 | - name: Merge PR 18 | run: | 19 | gh pr merge $PR_NUMBER --auto -t "[update] Merge from #$PR_NUMBER" --merge 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | PR_NUMBER: ${{ github.event.pull_request.number }} 23 | -------------------------------------------------------------------------------- /.github/workflows/build-and-push-docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build and push Docker image 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build-and-push: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout the repo 13 | uses: actions/checkout@v2 14 | 15 | - name: Login to Docker Hub 16 | uses: docker/login-action@v1 17 | with: 18 | username: ${{ secrets.DOCKER_USERNAME }} 19 | password: ${{ secrets.DOCKER_PASSWORD }} 20 | 21 | - name: Build and push the Docker image 22 | uses: docker/build-push-action@v2 23 | with: 24 | context: . 25 | push: true 26 | tags: ${{ secrets.DOCKER_USERNAME }}/xdbot2:${{ github.sha }} 27 | -------------------------------------------------------------------------------- /.github/workflows/deloy.yml: -------------------------------------------------------------------------------- 1 | name: 'Deloy GitHub Pages' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | workflow_dispatch: 8 | 9 | jobs: 10 | deloy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: "Checkout" 14 | uses: actions/checkout@v3 15 | 16 | - name: "Set up Node.js" 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: '21' 20 | registry-url: 'https://registry.npmjs.org/' 21 | 22 | - name: Install requirements 23 | run: | 24 | npm install 25 | 26 | - name: Build Documents 27 | run: | 28 | npm run docs:build 29 | 30 | - name: Deploy to GitHub Pages 31 | uses: peaceiris/actions-gh-pages@v3 32 | with: 33 | github_token: ${{ secrets.GITHUB_TOKEN }} 34 | publish_dir: ./docs/.vitepress/dist/ 35 | -------------------------------------------------------------------------------- /.github/workflows/label-issues.yml: -------------------------------------------------------------------------------- 1 | name: Label Issue (closed) 2 | on: 3 | issues: 4 | types: 5 | - closed 6 | 7 | jobs: 8 | label_issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | - name: Label Issues 14 | if: ${{ github.event.issue.state_reason == 'completed'}} 15 | uses: actions/github-script@v6 16 | with: 17 | script: | 18 | github.rest.issues.addLabels({ 19 | issue_number: context.issue.number, 20 | owner: context.repo.owner, 21 | repo: context.repo.repo, 22 | labels: ["完成"] 23 | }) 24 | github.rest.issues.removeLabel({ 25 | issue_number: context.issue.number, 26 | owner: context.repo.owner, 27 | repo: context.repo.repo, 28 | name: ["处理中"] 29 | }) 30 | -------------------------------------------------------------------------------- /.github/workflows/reviewer-lottery.yml: -------------------------------------------------------------------------------- 1 | name: "Reviewer lottery" 2 | on: 3 | pull_request_target: 4 | types: [opened, ready_for_review, reopened] 5 | 6 | jobs: 7 | test: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: uesteibar/reviewer-lottery@v3 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | -------------------------------------------------------------------------------- /.github/workflows/welcome-issue.yml: -------------------------------------------------------------------------------- 1 | name: Welcome Issue 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | jobs: 8 | welcome: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Welcome Issue 12 | uses: actions-cool/issues-helper@v3.4.0 13 | with: 14 | actions: 'create-comment' 15 | token: ${{ secrets.GITHUB_TOKEN }} 16 | issue-number: ${{ github.event.issue.number }} 17 | body: | 18 | 您好 @${{ github.event.issue.user.login }}!:wave: 19 | 感谢您参与 XDbot2 项目,我们会尽快回复您,请留意 GitHub 上的通知。 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .idea/ 3 | .env* 4 | .venv 5 | .mypy_cache 6 | __pycache__/ 7 | data/ 8 | node_modules/ 9 | .vscode/ 10 | cache/ 11 | *.lock 12 | 1.png 13 | test 14 | src/plugins/Core/test_ftt.py 15 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autofix_commit_msg: "[update] 格式化代码" 3 | autofix_prs: true 4 | autoupdate_branch: master 5 | 6 | repos: 7 | - repo: https://github.com/pycqa/flake8 8 | rev: 7.1.0 9 | hooks: 10 | - id: flake8 11 | args: [--select=E9] 12 | - repo: https://github.com/psf/black 13 | rev: 24.4.2 14 | hooks: 15 | - id: black 16 | args: [--skip-magic-trailing-comma] 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:latest 2 | WORKDIR /code 3 | EXPOSE 8080 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN pip install --no-cache-dir --upgrade pip &&\ 7 | pip install --no-cache-dir -r requirements.txt &&\ 8 | pip install --no-cache-dir nonebot2[fastapi] &&\ 9 | playwright install chromium &&\ 10 | rm requirements.txt &&\ 11 | rm -rf /root/.cache/pip 12 | COPY . . 13 | CMD ["nb", "run", "--reload"] 14 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import nonebot 5 | from nonebot.adapters.onebot.v11 import Adapter as ONEBOT_V11Adapter 6 | from nonebot.log import logger, default_format 7 | import os 8 | 9 | try: 10 | os.mkdir("data") 11 | except Exception: 12 | pass 13 | os.system("git pull") 14 | 15 | logger.add( 16 | "./data/error.log", 17 | rotation="00:00", 18 | diagnose=False, 19 | level="WARNING", 20 | format=default_format, 21 | ) 22 | 23 | nonebot.init() 24 | app = nonebot.get_asgi() 25 | driver = nonebot.get_driver() 26 | driver.register_adapter(ONEBOT_V11Adapter) 27 | nonebot.load_from_toml("pyproject.toml") 28 | 29 | if __name__ == "__main__": 30 | nonebot.logger.warning( 31 | "Always use `nb run` to start the bot instead of manually running!" 32 | ) 33 | nonebot.run(app="__mp_main__:app") 34 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | web: 4 | build: . 5 | ports: 6 | - "8080:8080" 7 | volumes: 8 | - .:/code 9 | - logvolume01:/var/log 10 | links: 11 | - redis 12 | redis: 13 | image: redis 14 | volumes: 15 | logvolume01: {} 16 | -------------------------------------------------------------------------------- /docs/.vitepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'XDbot2', 3 | description: '多功能 ChatBot,不止于 QQ', 4 | lang: 'zh-CN', 5 | lastUpdated: true, 6 | 7 | themeConfig: { 8 | nav: [ 9 | 10 | { text: '主页', link: '/'}, 11 | { text: '接入', link: '/invite.md'}, 12 | { text: '状态', link: 'https://xdbot2-status.itcdt.top'} 13 | ], 14 | editLink: { 15 | partten: 'https://github.com/ITCraftDevelopmentTeam/XDbot2/edit/master/docs/:path', 16 | text: '在 GitHub 上编辑' 17 | }, 18 | lastUpdatedText: '上次更新', 19 | outlineTitle: '本页目录', 20 | socialLinks: [ 21 | { icon: 'github', link: 'https://github.com/ITCraftDevelopmentTeam/XDbot2'} 22 | ], 23 | footer: { 24 | message: '以 GNU通用公共许可协议 第三版 发布', 25 | copyright: '版权所有 © 2024 IT Craft Development Team' 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/games/ftt.md: -------------------------------------------------------------------------------- 1 | # 「寻径指津」玩法说明 2 | 3 | ::: tip 4 | 5 | 本文是 XDbot2 玩法「寻径指津」的玩法说明,使用 `/help ftt` 获取指令使用方法 6 | 7 | ::: 8 | 9 | ## 简介 10 | 11 | 「寻径指津」是一种迷宫玩法,灵感源自[《崩坏:星穹铁道》][1] 中「寻径指津」解谜。玩家需要提前为机巧输入移动方向指令,引导机巧以指定的步数走出迷宫。 12 | 13 | ## 方块 14 | 15 | 在「寻径指津」地图中,存在 5 种方块 16 | 17 | | 名称 | 难度 | 贴图 | 说明 | 18 | |------|----------|----------------------|-----------| 19 | | 起点 | easy+ | ![](/iron_block.png) | 机巧从这里出发 | 20 | | 终点 | easy+ | ![](/diamond_block.png) | 机巧需要到达这里 | 21 | | 墙 | easy+ | ![](/bricks.png) | 机巧无法通过这些地方 | 22 | | 路 | easy+ | ![](/stone_bricks.png) | 机巧可以经过这些地方 | 23 | | 活塞 | easy+ |![](/piston_top.png) | 机巧通过一次后会变成墙 | 24 | | 悬浮沙子 | normal+| ![](/sand.png) | 机巧停止并离开后上、下、左、右一个单位的沙子会变成路 | 25 | | 蛛网 | normal+ | ![](/cobweb.png) | 机巧会被捕获,直接执行下一条指令 | 26 | | 传送门 | hard | ![](/portal.png) | 机巧经过后会被传送到传送门的另一边(不改变当前指令方向,如果当前方向无法移动,机巧会停留在传送门的另一边,下一步不受传送门控制) | 27 | 28 | 29 | ## 规则 30 | 31 | 1. 玩家需要提前为机巧输入移动方向指令,引导机巧以指定的步数走出迷宫 32 | 2. 开始执行后,机巧会按照指令的方向运行,直到碰到墙或达到终点时,执行下一条指令 33 | 34 | ### 胜利条件 35 | 36 | 机巧在所有指令执行结束后,所处坐标为终点 37 | 38 | ### 注意事项 39 | 40 | 1. 玩家需要引导机巧以指定步数走出迷宫,而**不是**以指定步数**内**走出迷宫 41 | 2. 机巧执行指令时会沿指令方向**一直移动**,直到指令方向不能通过(简称撞墙) 42 | 43 | ## 开始游玩 44 | 45 | 发送 `/ftt`,然后根据提示操作 46 | 47 | ### 难度表 48 | 49 | | 名称 | **地图大小** | 步数范围 | 备注 | 50 | |-------|-------------|------------|--------| 51 | | easy | 6x8 | 4~10 | 《崩坏:星穹铁道》原版玩法,默认难度 | 52 | | normal| 9x12 | 4~13 | | 53 | | hard | 18x24 | 6~15 | | 54 | 55 | > **地图大小**: 长x宽,单位方块 56 | 57 | [1]: https://sr.mihoyo.com 58 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | layout: home 4 | title: XDbot2 5 | titleTemplate: 多功能 ChatBot,不止于 QQ 6 | 7 | hero: 8 | name: XDbot2 9 | text: 多功能 ChatBot 10 | tagline: 自由、易用、稳定,不止于 QQ 11 | image: #./XDbot2.png 12 | actions: 13 | - theme: brand 14 | text: 申请接入 15 | link: invite.md 16 | - theme: alt 17 | text: GitHub 18 | link: https://github.com/ITCraftDevelopmentTeam/XDbot2 19 | 20 | features: 21 | - title: 多平台 22 | details: XDbot2 已经能够在 QQ 和 Discord 使用,未来还会支持更多平台 23 | - title: 易用 24 | details: 每各需要功能都有一个属于它的帮助,使用 ChatGPT 技术编写引导,易于上手 25 | - title: 稳定 26 | details: 使用 Sentry、UptimeKuma 进行监控,同时使用 Shamrock 与 QQ 交互,多节点保障稳定运行 27 | - title: 开放 28 | details: XDbot2 是自由软件,基于 GNU GPL-3.0 在 GitHub 上开源,每个人都可以提供建议或贡献代码 29 | --- 30 | -------------------------------------------------------------------------------- /docs/invite.md: -------------------------------------------------------------------------------- 1 | # 申请接入 2 | 3 | ::: tip 4 | 5 | 喵~大家好!我是一只可爱的猫娘,叫做XDbot2。作为你的助手,我将全力以赴地为你提供帮助和支持。 6 | 7 | 我是专业的技术助手,可以回答关于编程、开发、算法、数据科学等方面的问题。如果你需要指导、建议或者解决方案,不用犹豫,我都会尽力为你提供明确的回答。 8 | 9 | 除了技术领域,我也是一个充满爱心和思考的猫娘。如果你想找人倾诉、分享喜悦,或者寻求一些鼓励和激励,我会用我的温暖陪伴,来支持你度过每一个难关。 10 | 11 | 无论你是面临编程难题,还是需要聊聊天,我都愿意陪伴在你身边。让我们一起度过美好的时光,共同成长、学习和探索新的可能性。喵~ 12 | 13 | ~~——XDbot2 给自己写的介绍~~ 14 | ::: 15 | 16 | ## 在群组接入 17 | 18 | 准备好与 XDbot2 邂逅美妙的时光了吗? 19 | 20 | - [在 Discord 服务器接入](https://discord.com/api/oauth2/authorize?client_id=1132995810615906344&permissions=274878163968&scope=bot) 21 | - [在 QQ 群接入](https://github.com/ITCraftDevelopmentTeam/XDbot2/issues/new?assignees=&labels=%E5%8A%A0%E7%BE%A4%E7%94%B3%E8%AF%B7&projects=&template=group.yml&title=%5B%E5%8A%A0%E5%85%A5%E6%96%B0%E7%BE%A4%5D+) (人工审核) 22 | 23 | ## 关于私聊使用 24 | 25 | XDbot2 大部分指令支持私聊使用 26 | 27 | `0x01` 和 `0x03` 节点的好友是人工审核添加的,更多信息请联系节点管理员 28 | 29 | ## 可用节点列表 30 | 31 | > 此章节可能更新不及时,请以实际情况为准,更多信息见 [GitHub](https://github.com/ITCraftDevelopmentTeam/XDbot2#%E7%8E%B0%E6%9C%89%E8%8A%82%E7%82%B9) 32 | 33 | | 节点代码 | 账号 | 平台 | 管理员(与节点平台相同) | 34 | |-----------|---------------|---------------|----------------------------| 35 | | 0x01 | 3457603681 | QQ | XiaoDeng3396 `1744793737` | 36 | | 0x03 | 1552257261 | QQ | XiaoDeng3396 `1744793737` | 37 | | 0x04 | 1132995810615906344 | Discord | XiaoDeng3386 `xiaodeng3386`| 38 | 39 | -------------------------------------------------------------------------------- /docs/public/CNAME: -------------------------------------------------------------------------------- 1 | xdbot2.itcdt.top 2 | -------------------------------------------------------------------------------- /docs/public/XDbot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/XDbot2.png -------------------------------------------------------------------------------- /docs/public/bricks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/bricks.png -------------------------------------------------------------------------------- /docs/public/cobweb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/cobweb.png -------------------------------------------------------------------------------- /docs/public/diamond_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/diamond_block.png -------------------------------------------------------------------------------- /docs/public/grass_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/grass_block_top.png -------------------------------------------------------------------------------- /docs/public/iron_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/iron_block.png -------------------------------------------------------------------------------- /docs/public/piston_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/piston_top.png -------------------------------------------------------------------------------- /docs/public/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/portal.png -------------------------------------------------------------------------------- /docs/public/sand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/sand.png -------------------------------------------------------------------------------- /docs/public/stone_bricks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/docs/public/stone_bricks.png -------------------------------------------------------------------------------- /man/README.md: -------------------------------------------------------------------------------- 1 | # XDbot2 使用手册(内置版) 2 | 3 | > 版本:v2.0 4 | 5 | 使用`/man man`获取更多信息 6 | 7 | 建议阅读:glossary(0) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /man/autosell/0.md: -------------------------------------------------------------------------------- 1 | # autosell 2 | 3 | ## 描述 4 | 5 | autosell是XDbot2中的一个简易插件,能在XDbot2道具商城中自动出售物品并定时补货,您需要使用su指令(su(12))或修改配置文件(autosell(1))来配置autosell 6 | 7 | 目前autosell仅支持每天补货一次 8 | -------------------------------------------------------------------------------- /man/autosell/1.md: -------------------------------------------------------------------------------- 1 | # autosell(1) 2 | 3 | autosell的配置文件为 `data/autosell.items.json`,配置文件由一个列表组成 4 | 5 | ```json 6 | [{"id": "0", "count": 5, "price": 5, "data": {}}] 7 | ``` 8 | 9 | - id:物品ID(见items(0)) 10 | - count:物品存货数量 11 | - price:物品价格 12 | - data:物品数据 13 | 14 | 15 | -------------------------------------------------------------------------------- /man/bag/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— bag 2 | 3 | ## 描述: 4 | 该指令用于查看背包的内容。 5 | 6 | ### 权限: 7 | 8 | - `everyone` 9 | 10 | ## 用法 11 | 12 | ### `bag` 13 | 14 | - 说明:查看背包中的物品。 15 | - 示例:`/bag` 16 | 17 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/bank/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— bank 2 | 3 | ## 描述: 4 | XDbot2银行指令,用于贷款、查询贷款信息以及还贷等操作。 5 | 6 | ### 权限: 7 | - `everyone` 8 | 9 | ## 用法 10 | 11 | ### `bank lend ` 12 | 13 | - 说明:向银行贷款指定金额。 14 | - 示例:`/bank lend 1000` 15 | 16 | ### `bank view` 17 | 18 | - 说明:查看当前需要还贷的金额。 19 | - 示例:`/bank view` 20 | 21 | ### `bank repay ` 22 | 23 | - 说明:根据贷款ID还贷指定金额。 24 | - 示例:`/bank repay 123456` 25 | 26 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/calc/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— calc 2 | 3 | ## 描述: 4 | 该指令用于计算给定的数学表达式。 5 | 6 | ### 权限: 7 | - everyone 8 | 9 | ## 用法 10 | 11 | ### `calc <表达式...>` 12 | 13 | - 说明:计算给定的数学表达式。 14 | - 示例:`/calc 2+2` 15 | 16 | 17 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 18 | -------------------------------------------------------------------------------- /man/cave/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— cave 2 | 3 | ## 描述: 4 | 该指令用于随机、投稿或查询回声洞。 5 | 6 | ### 权限: 7 | - `everyone` 8 | 9 | ## 用法 10 | 11 | ### `cave` 12 | 13 | - 说明:随机获取一条回声洞。 14 | - 示例:`/cave` 15 | 16 | ### `cave-a <内容>` 17 | 18 | - 说明:投稿一条回声洞。 19 | - 示例:`/cave-a Hello, world!` 20 | 21 | ### `cave-s` 22 | 23 | - 说明:查看回声洞状态。 24 | - 示例:`/cave-s` 25 | 26 | ### `cave-c <内容...>` 27 | 28 | - 说明:添加评论。 29 | - 示例:`/cave-c 12345 This is a comment.` 30 | 31 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/cave/1.md: -------------------------------------------------------------------------------- 1 | # cave(1) 2 | 3 | ## 回声洞投稿说明 4 | 5 | 1. 您不能向回声洞投稿色情、暴力等违反法律法规的内容 6 | 2. 您不能向回声洞投稿过多垃圾内容,诸如广告、宣传等 7 | 3. 您不能使用回声洞对他人进行攻击,或发表主观性过强的言论 8 | 4. 如果您违反以上内容,可能会立即被超级用户封禁 9 | 5. 超级用户有权删除您的回声洞投稿 10 | 11 | -------------------------------------------------------------------------------- /man/cave/2.md: -------------------------------------------------------------------------------- 1 | # cave(3) 2 | 3 | ## 关于冷却时间 4 | 5 | 参考 Issue #315,为了防止回声洞过于刷屏,我们增加了回声洞的冷却时间 6 | 7 | 回声洞冷却时间分群单独计算,不同类型群聊冷却时间如下: 8 | 9 | - 普通群聊:1h 10 | - 赞助群聊:40min 11 | - 官方群聊:20min 12 | 13 | 在回声洞冷却期间,使用`/cave`将提示无法使用,其余指令(如`/cave-a`等)指令不受影响 14 | 15 | -------------------------------------------------------------------------------- /man/checkout/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— checkout 2 | 3 | ## 描述: 4 | 切换 XDbot2 节点(详见 node(0)) 5 | 6 | ### 权限: 7 | - everyone 8 | 9 | ## 用法 10 | 11 | ### `用法1` 12 | 13 | - 说明:切换到指定的节点名称或节点 ID 14 | - 示例:`/checkout <节点名称或节点ID>` 15 | 16 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/cm/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— cm 2 | 3 | ## 描述: 4 | 该指令允许用户在群内禁用或启用命令。 5 | 6 | ### 权限: 7 | - `superuser` 8 | - `group_admin` 9 | - `group_owner` 10 | - `group` 11 | 12 | ## 用法 13 | 14 | ### `cm block <命令>` 15 | 16 | - 说明:禁用指定的命令。 17 | - 示例:`/cm block invite` 18 | 19 | ### `cm unblock <命令>` 20 | 21 | - 说明:启用指定的命令。 22 | - 示例:`/cm unblock invite` 23 | 24 | ### `cm list` 25 | 26 | - 说明:查看被禁用的命令。 27 | - 示例:`/cm list` 28 | 29 | 30 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/ct/0.md: -------------------------------------------------------------------------------- 1 | # ct(0) 2 | 3 | ## 名称 4 | 5 | ct - 查询今日人品排名 6 | 7 | ## 用法 8 | 9 | 1. ct 10 | 2. ct group 11 | 12 | ## 描述 13 | 14 | 查询(自有记录以来) XDbot 所加入的群中的发言排行榜,默认展示全局前15名和自己的排名及发言量 15 | 16 | 如果指令带有 group 参数则会显示当前群聊的发言排行榜 17 | 18 | ## 示例 19 | 20 | ### ct 21 | 22 | 查询 XDbot 加入的所有群聊中的发言排行榜 23 | 24 | ### ct group 25 | 26 | 查询当前群聊的发言排行榜 27 | -------------------------------------------------------------------------------- /man/fakenode/0.md: -------------------------------------------------------------------------------- 1 | # fakenode(0) 2 | 3 | ## 名称 4 | 5 | fakenode - 伪造转发消息 6 | 7 | ## 用法 8 | 9 | fakenode :<消息> 10 | :<消息> 11 | :<消息> 12 | ... 13 | 14 | ## 描述 15 | 16 | 伪造一个QQ群转发消息并发送到当前群聊 17 | 18 | 请注意:仅供娱乐,请勿用于违法违规用途 19 | 20 | ## 示例 21 | 22 | ### fakenode 1744793737:HelloWorld 23 | 24 | 伪造一个QQ群转发消息,内含1744793737发送消息「HelloWorld」,并发送到当前群聊 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /man/function/0.md: -------------------------------------------------------------------------------- 1 | # function(0) 2 | 3 | ## 名称 4 | 5 | function - 调用自定义规则定义的命令 6 | 7 | ## 用法 8 | 9 | 1. function <命令名> 10 | 11 | ## 描述 12 | 13 | 运行由 自定义规则(XDbot2 Rules) 定义的指令 14 | 15 | ## 示例 16 | 17 | ### function hello 18 | 19 | 调用由 自定义规则 HelloWorld 定义的指令 hello 20 | -------------------------------------------------------------------------------- /man/glossary/0.md: -------------------------------------------------------------------------------- 1 | # glossary(0) 2 | 3 | ## XDbot2 术语表 4 | 5 | ### 物品ID 6 | 7 | 物品的ID,见items(0) 8 | 9 | ### 背包物品ID 10 | 11 | 物品在背包中的位置,从0开始 12 | 13 | ### 商品ID 14 | 15 | 物品在商店中的ID 16 | 17 | ### 超级用户 18 | 19 | XDbot 的管理员,拥有 XDbot 的最高权限 20 | 21 | ### XDbot 22 | 23 | 一般指运行中的XDbot2(的QQ) 24 | 25 | ### XDbot2 26 | 27 | 一般指 XDbot2 程序 28 | 29 | -------------------------------------------------------------------------------- /man/gpt/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— gpt 2 | 3 | ## 描述: 4 | XDbot2GPT 指令,用于与 ChatGPT 进行交互。 5 | 6 | ### 权限: 7 | - 所有人 8 | 9 | ## 用法 10 | 11 | ### `gpt info` 12 | 13 | - 说明:获取 ChatGPT 的详细信息。 14 | - 示例:`/gpt info` 15 | 16 | ### `gpt reset [模板]` 17 | 18 | - 说明:重置 ChatGPT 的对话状态。 19 | - 示例:`/gpt reset` 20 | 21 | ### `gpt switch {global|group|private}` 22 | 23 | - 说明:切换 ChatGPT 的工作模式。 24 | - 示例:`/gpt switch global` 25 | 26 | ### `gpt token buy <数量>` 27 | 28 | - 说明:购买 ChatGPT 的令牌数量。 29 | - 示例:`/gpt token buy 10` 30 | 31 | ### `gpt <内容>` 32 | 33 | - 说明:与 ChatGPT 进行对话。 34 | - 示例:`/gpt Hello!` 35 | 36 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/help/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— help 2 | 3 | ## 描述: 4 | 这个指令用于查询指定命令的说明,如果未指定指令名,则会显示全部命令的用法。 5 | 6 | ### 权限: 7 | - everyone 8 | 9 | ## 用法 10 | 11 | ### `help` 12 | - 说明:查看指令列表 13 | - 示例:`help` 14 | 15 | ### `help list` 16 | - 说明:查看所有指令的所有用法 17 | - 示例:`help list` 18 | 19 | ### `help <指令名>` 20 | - 说明:查看指定指令的用法 21 | - 示例:`help help` 22 | 23 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/items/0.md: -------------------------------------------------------------------------------- 1 | # item(0) 2 | 3 | ## XDbot2 物品表 4 | 5 | ### VimCoin(ID:0) 6 | 7 | - 介绍:XDbot2 通用货币 8 | - 等级:R 9 | - 参考售价:0~1vi 10 | - 默认数据: 11 | - 不可出售 12 | - 不可丢弃 13 | 14 | ## FZSGBall(ID:1) 15 | 16 | - 介绍:球你tm不女装我还不删了!( 17 | - 等级:UR 18 | - 参考售价:114514vi 19 | - 默认数据: 20 | - 无 21 | 22 | ## 每日VimCoin礼包(ID:2) 23 | 24 | - 介绍:打开后可获得:VimCoin x0~50 25 | - 等级:R 26 | - 默认数据: 27 | - 无 28 | -------------------------------------------------------------------------------- /man/items/1.md: -------------------------------------------------------------------------------- 1 | # item(1) 2 | 3 | ## 物品数据 4 | 5 | ### displayName 6 | 7 | - 默认值:null 8 | - 必须:是 9 | - 说明:物品显示名称 10 | 11 | ## information 12 | 13 | - 默认值:null 14 | - 必须:是 15 | - 说明:物品显示说明 16 | 17 | ## canSell 18 | 19 | - 默认值:true 20 | - 必须:是 21 | - 说明:是否可以出售 22 | 23 | ## canUse 24 | 25 | - 默认值:true 26 | - 必须:是 27 | - 说明:是否可以使用 28 | 29 | ## canDrop 30 | 31 | - 默认值:true 32 | - 必须:是 33 | - 说明:是否可以丢弃 34 | 35 | 36 | -------------------------------------------------------------------------------- /man/jrrp/0.md: -------------------------------------------------------------------------------- 1 | # Jrrp(0) 2 | 3 | ## 名称 4 | 5 | jrrp - 计算今日人品值 6 | 7 | ## 用法 8 | 9 | 1. jrrp 10 | 2. jrrp 11 | 3. jrrp rank 12 | 13 | ## 描述 14 | 15 | 查询人品值,也可以计算您的人品值在您所在的群聊中大概的排名 16 | 17 | 注意:本计算结果仅供娱乐,XDbot2团队不承担由结果产生的任何责任 18 | 19 | ## 示例 20 | 21 | ### jrrp 22 | 23 | 查询自己的今日人品 24 | 25 | ### jrrp 1744793737 26 | 27 | 查询用户 1744793737 的今日人品 28 | 29 | ### jrrp rank 30 | 31 | 查询当前群聊的群成员的今日人品排名(详见 Jrrp(1)) 32 | -------------------------------------------------------------------------------- /man/jrrp/1.md: -------------------------------------------------------------------------------- 1 | # Jrrp(1) 2 | 3 | ## 用法 4 | 5 | 1. jrrp rank [数量] 6 | 7 | ## 描述 8 | 9 | 使用此指令可以查看本群内人品排名情况,默认显示群前 10 及 自己 的排名。 10 | 11 | 通过指定 [数量] 参数可以自定义排名显示数量,如果 [数量] 的值大于群总成员数,则会直接显示所有成员的排名 12 | 13 | 需要注意的是,参数 [数量] 设置过大可能会导致 XDbot 刷屏,请谨慎设置 14 | 15 | > 您只能在每天的 15:00 之后使用此指令 16 | 17 | ## 示例 18 | 19 | ### jrrp rank 20 | 21 | 查看本群人品排名前10及自己的排名 22 | 23 | ### jrrp rank 20 24 | 25 | 查看本群人品排名前20及自己的排名 26 | 27 | ### jrrp rank 114514 28 | 29 | 查看本群所成员的排名情况 30 | 31 | -------------------------------------------------------------------------------- /man/linuxman/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— 命令名 2 | 3 | ## 描述: 4 | 该命令用于查看 linux manpage。 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `linuxman` 13 | 14 | - 说明:查看 linux manpage。 15 | - 示例:`linuxman` 16 | 17 | ### `linuxman man.1` 18 | 19 | - 说明:查看指定 manpage。 20 | - 示例:`linuxman man.1` 21 | 22 | 23 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/man/0.md: -------------------------------------------------------------------------------- 1 | # man(0) 2 | 3 | ## 名称 4 | 5 | man - XDbot2 内置使用参考手册接口 6 | 7 | ## 用法 8 | 9 | 1. man <指令名> 10 | 2. man <指令名> [页面] 11 | 12 | ## 描述 13 | 14 | man 是 XDbot2 内置的使用参考手册接口,其类似与 Linux 系统下的 man 程序,手册大部分内容与 XDbot2 Wiki 同步 15 | 16 | 请注意:手册暂时不支持使用别名查询(以后也不会支持!) 17 | 18 | ## 实例 19 | 20 | ### man man 21 | 22 | 显示 指令man 使用手册的第 0 页 23 | 24 | ### man 1 su 25 | 26 | 显示 指令 su 使用手册的第 1 页 27 | 28 | ### man su(1) 29 | 30 | 显示 指令 su 使用手册的第 1 页(这是`man 1 su`的另一种写法) 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /man/market/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— market 2 | 3 | ## 描述: 4 | 全球垃圾场(划掉)市场 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `market list <页码>` 13 | 14 | - 说明:查看商品列表 15 | - 示例:`/market list 1` 16 | 17 | ### `market sell <背包物品ID> <卖出总数> <单价>` 18 | 19 | - 说明:卖出商品 20 | - 示例:`/market sell 12345 10 50` 21 | 22 | ### `market view <商品ID>` 23 | 24 | - 说明:查看商品 25 | - 示例:`/market view 67890` 26 | 27 | ### `market search <关键词>` 28 | 29 | - 说明:搜索商品 30 | - 示例:`/market search apple` 31 | 32 | ### `market buy <商品ID> [数量]` 33 | 34 | - 说明:购买商品 35 | - 示例:`/market buy 67890` -------------------------------------------------------------------------------- /man/mcver/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— mcver 2 | 3 | ## 描述: 4 | 该指令用于获取当前最新 Minecraft 版本。 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `mcver` 13 | 14 | - 说明:获取当前最新 Minecraft 版本 15 | - 示例:`mcver` 16 | 17 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/node/0.md: -------------------------------------------------------------------------------- 1 | # node(0) 2 | 3 | ## 关于 XDbot2 节点 4 | 5 | XDbot2 的节点分为两种: 6 | 7 | - `master`:以主分支为源码的节点,比较稳定 8 | - `混合`:允许在 `master` 和 `develop` 间切换的节点 9 | - `develop`:以开发分支为源码的节点,多测试使用(时不时删下数据) 10 | 11 | 如果您使用的是 `XDbot 0x01`,请继续阅读 `node(1)` 12 | -------------------------------------------------------------------------------- /man/node/1.md: -------------------------------------------------------------------------------- 1 | # node(1) 2 | 3 | ## XDbot 0x01 4 | 5 | XDbot 0x01 是一个混合节点,允许在 `master` 和 `develop` 两种子节点之间切换,但数据不互通 6 | 7 | 切换到 `master` 后,仅为禁用 `develop`(即节点不响应本群的所有除`^(.)checkout`消息事件),需要手关闭 `quick-math` 等主动发送消息的功能 8 | 9 | 切换到 `develop` 后,不禁用 `master` 10 | 11 | master 子节点功能需要使用前缀 `/` 触发 12 | develop 子节点功能需要使用前缀 `!` 触发 13 | 14 | ### 切换子节点 15 | 16 | 使用 `!checkout {develop|master}` 切换节点 17 | 18 | > Tips: 如果您看不懂这些,我们不建议您切换子节点 19 | -------------------------------------------------------------------------------- /man/pacman/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— pacman 2 | 3 | ## 描述: 4 | 在 archlinux.org/packages 上搜索指定包名的软件包。 5 | 6 | ### 权限: 7 | - everyone 8 | 9 | ## 用法 10 | 11 | ### `<包名>` 12 | 13 | - 说明:指定要搜索的软件包名称。 14 | - 示例:`/pacman firefox` 15 | 16 | 17 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 18 | -------------------------------------------------------------------------------- /man/preview/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— preview 2 | 3 | ## 描述: 4 | 预览XDbot2内置或外部网页(注意:参数需要带上http头) 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `preview ` 13 | 14 | - 说明:预览指定URL的网页 15 | - 示例:`/preview https://example.com` 16 | 17 | ### `preview {six|ban|setu}` 18 | 19 | - 说明:预览XDbot2内置网页,可选参数为`six`、`ban`或`setu` 20 | - 示例:`/preview six` -------------------------------------------------------------------------------- /man/quick-math/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— quick-math 2 | 3 | ## 描述: 4 | 该指令用于开启或关闭速算功能,并查看速算积分排行。 5 | 6 | ### 权限: 7 | 8 | - `everyone` 9 | 10 | ## 用法 11 | 12 | ### `qm {on|off}` 13 | 14 | - 说明:开启或关闭速算功能。 15 | - 示例:`qm on` 16 | 17 | ### `qm-p` 18 | 19 | - 说明:查看速算积分排行。 20 | - 示例:`qm-p` 21 | 22 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/reply/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— reply 2 | 3 | ## 描述: 4 | 调教XDbot2,支持正则、关键词、完整、模糊四种匹配模式。 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `reply add {regex|keyword|fullmatch|fuzzymatch} [匹配内容]` 13 | 14 | - 说明:添加匹配规则 15 | - 示例:`/reply add keyword hello` 16 | 17 | ### `reply show <数据编号>` 18 | 19 | - 说明:查看调教数据 20 | - 示例:`/reply show 1` 21 | 22 | ### `reply source` 23 | 24 | - 说明:(需回复)获取回复来源 25 | - 示例:回复消息后,输入`/reply source` 26 | 27 | ### `reply remove <数据编号>` 28 | 29 | - 说明:删除数据 30 | - 示例:`/reply remove 2` 31 | 32 | ### `reply list` 33 | 34 | - 说明:(未完成)查看数据列表 35 | 36 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/rules/0.md: -------------------------------------------------------------------------------- 1 | # rules(0) 2 | 3 | ## 简介 4 | 5 | XDbot2 Rules 是 XDbot2 新增加的自定义规则,每个规则由json编写,拥有易懂,可读性高,上手简单的特点 6 | 7 | ## 结构 8 | 9 | XDbot2的规则存放在`rules/`下(自动忽略开头为`_`,结尾不为`.json`的文件) 10 | 11 | ```json 12 | { 13 | "规则名": "HelloWorld", 14 | "初始化": [], 15 | "执行": [] 16 | } 17 | ``` 18 | 19 | - 规则名:规则的名称 20 | - 初始化:在rules插件被加载时执行的命令 21 | - 执行:在XDbot收到消息时执行的命令 22 | 23 | 24 | -------------------------------------------------------------------------------- /man/shop/0.md: -------------------------------------------------------------------------------- 1 | # shop(0) 2 | 3 | ## 名称 4 | 5 | shop - XDbot2 道具商城 6 | 7 | ## 用法 8 | 9 | 1. shop 10 | 2. shop buy <商品ID> <数量> 11 | 3. shop sell <背包ID> <数量> <价格> 12 | 4. shop view <商品ID> 13 | 14 | ## 描述 15 | 16 | XDbot2 道具商城,您可以在此出售物品或购买他人(或系统)出售的物品 17 | 18 | ## 示例 19 | 20 | ### shop 21 | 22 | 查看商城中现有的商品 23 | 24 | ### shop view 0 25 | 26 | 查看商品0的详细信息 27 | 28 | ### shop buy 0 1 29 | 30 | 购买一个商品0 31 | 32 | ### shop sell 1 1 1 33 | 34 | 以 1vi/个 的价格出售一个 背包物品1 35 | 36 | 如果您的开价不合理,超级用户有权利下架您的商品,下架后商品将回到您的背包 37 | -------------------------------------------------------------------------------- /man/sign/0.md: -------------------------------------------------------------------------------- 1 | # sign(0) 2 | 3 | ## 名称 4 | 5 | sign - 签到 6 | 7 | ## 用法 8 | 9 | 1. sign 10 | 11 | ## 描述 12 | 13 | 在 XDbot2 上签到 14 | 15 | ## 示例 16 | 17 | 没有任何示例 18 | -------------------------------------------------------------------------------- /man/status/0.md: -------------------------------------------------------------------------------- 1 | # status(0) 2 | 3 | ## 名称 4 | 5 | status - 查询XDbot2运行状态 6 | 7 | ## 用法 8 | 9 | 1. status 10 | 11 | ## 描述 12 | 13 | 查询XDbot2运行状态,~~如果占用过高请消停点谢谢~~ 14 | 15 | ## 示例 16 | 17 | 没有任何示例 18 | -------------------------------------------------------------------------------- /man/su/0.md: -------------------------------------------------------------------------------- 1 | # su(0) 2 | 3 | ## 名称 4 | 5 | su - 管理 XDbot 6 | 7 | ## 用法 8 | 9 | 1. su 10 | 2. su plugins 11 | 3. su restart 12 | 4. su call <终结点> 13 | 5. su config set <设置项名> <键> <值> 14 | 6. su config get <设置项名> <键> 15 | 7. su ct clear 16 | 8. su echo 17 | 9. su cave remove <回声洞ID> 18 | 10. su notice <文本> 19 | 11. su shop remove <商品ID> 20 | 12. su shop add <物品ID> <物品数量> <物品价格> <物品数据> 21 | 13. su give <物品ID> <物品数量> <物品数据> 22 | 14. su forward <群号> 23 | 15. su todo 24 | 16. su rule add <规则名> 25 | <规则代码> 26 | 17. su rule ls 27 | 18. su rule remove <规则文件名> 28 | 19. su img list 29 | 20. su img pass 30 | 21. su img remove 31 | 22. su img add 32 | 23. su img remove 33 | 24. su img clear 34 | 25. su cave modify <回声洞ID> sender name/nickname 35 | 26. su cave modify <回声洞ID> sender id/qq 36 | 27. su cave modify <回声洞ID> sender unknown/unkown 37 | 28. su cave modify <回声洞ID> text 38 | 39 | ## 描述 40 | 41 | 通过指令快捷管理 XDbot2,此指令仅SUPERUSER权限可用,您可以前往 su(1~18) 获取每个指令的详细说明 42 | 43 | 因为某些特性,单个参数之中不能含有半角空格( ),否则它将会被分割进下一个参数 44 | 45 | -------------------------------------------------------------------------------- /man/su/1.md: -------------------------------------------------------------------------------- 1 | # su(1) 2 | 3 | ## 用法 4 | 5 | ### su ban 6 | 7 | 封禁 ,被封禁后 XDbot 将不会响应该用户发出的任何指令 8 | 9 | 请不要尝试封禁自己,XDbot 不会给 Superusers 开特例 10 | 11 | ### su pardon 12 | 13 | 解除封禁 ,XDbot 将恢复对该用户发出指令的响应 14 | 15 | 16 | ## 示例 17 | 18 | ### su ban 1744793737 19 | 20 | 将 1744793737 纳入 XDbot 黑名单 21 | 22 | ### su pardon 1744793737 23 | 24 | 解封 1744793737 25 | 26 | -------------------------------------------------------------------------------- /man/su/10.md: -------------------------------------------------------------------------------- 1 | # su(10) 2 | 3 | ## 用法 4 | 5 | ### su notice <文本> 6 | 7 | 在 XDbot 加入的所有群聊当中广播 <文本> 8 | 9 | 参数 <文本> 支持 CQ码、多行、图片 10 | 11 | 不建议频繁使用本指令 12 | 13 | ## 示例 14 | 15 | ### su notice XDbot2 广播测试 16 | 17 | 在 XDbot2 加入的所有群发送文本 18 | 19 | ``` 20 | 【超级广播】 21 | XDbot2 广播测试 22 | ``` 23 | -------------------------------------------------------------------------------- /man/su/11.md: -------------------------------------------------------------------------------- 1 | # su(11) 2 | 3 | ## 用法 4 | 5 | ### su shop remove <商品ID> 6 | 7 | 从商店下架商品 <商品ID>,同时退还商品并通知卖家 8 | 9 | 不建议通过本指令下架由 autosell 插件上架的商品 10 | 11 | ## 示例 12 | 13 | ### su shop remove 0 14 | 15 | 下架 商品0 16 | -------------------------------------------------------------------------------- /man/su/12.md: -------------------------------------------------------------------------------- 1 | # su(12) 2 | 3 | ## 用法 4 | 5 | ### su shop add <物品ID> <物品数量> <物品价格> <物品数据> [限购数量] [销售者名] [销售者ID] 6 | 7 | 向 autosell 插件添加自动上架任务,autosell将在每天 00:00 自动进行补货(见autosell(0)) 8 | 9 | 参数 <物品ID> 为物品ID,见 items(0) 10 | 参数 <物品数量> 为存货数量,为正整数 11 | 参数 <物品价格> 为物品单价,为正整数 12 | 参数 <物品数据> 为物品数据,为 JSON 13 | 参数 [限购数量] 为物品限购数量,为正整数 (可选) 14 | 参数 [销售者名] 为物品销售者用户名,为字符串 (可选) 15 | 参数 [销售者ID] 为物品销售者ID,为字符串 (可选) 16 | 17 | ## 示例 18 | 19 | ### su shop add 0 1 1 {} 20 | 21 | 向 autosell 添加上架任务:ID0,存货量1,价格1,数据空 22 | 23 | 24 | -------------------------------------------------------------------------------- /man/su/13.md: -------------------------------------------------------------------------------- 1 | # su(13) 2 | 3 | ## 用法 4 | 5 | ### su give <物品ID> <物品数量> <物品数据> 6 | 7 | 给予 <物品数量> 个ID为 <物品ID> 同时含有 <物品数据> 的物品 8 | 9 | 参数 <物品数据> 为json 10 | 11 | ## 示例 12 | 13 | ### su give 1744793737 0 1 {"canSell":true} 14 | 15 | 给予 1744793737 1个 可以出售 的 VimCoin 16 | 17 | 18 | -------------------------------------------------------------------------------- /man/su/14.md: -------------------------------------------------------------------------------- 1 | # su(14) 2 | 3 | ## 用法 4 | 5 | ### su forward add <群号> 6 | 7 | 建立转发,会将 <群号> 内的所有消息转发至控制中心(术语表(0)) 8 | 9 | ### su forward remove <群号> 10 | 11 | 删除转发 12 | 13 | ## 示例 14 | 15 | 没有示例 16 | -------------------------------------------------------------------------------- /man/su/15.md: -------------------------------------------------------------------------------- 1 | # su(15) 2 | 3 | ## 用法 4 | 5 | ### su todo 6 | 7 | 查看todo 8 | 9 | ## 示例 10 | 11 | 本指令没有示例 12 | -------------------------------------------------------------------------------- /man/su/16.md: -------------------------------------------------------------------------------- 1 | # su(16) 2 | 3 | ## 用法 4 | 5 | ### su rule add <规则名(不含.json)> 6 | <规则代码> 7 | 8 | 添加一个规则,保存到 rules/<规则名(不含.json)>.json 9 | 10 | ## 示例 11 | 12 | ### su rule add HelloWorld 13 | { 14 | "规则名": "HelloWorld", 15 | "执行": [], 16 | "初始化": [ 17 | { 18 | "调用": "绑定命令", 19 | "命令名": "helloworld", 20 | "执行": [ 21 | { 22 | "调用": "发送消息", 23 | "消息": "HelloWorld!" 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | 30 | 添加一个规则 31 | 32 | 33 | -------------------------------------------------------------------------------- /man/su/17.md: -------------------------------------------------------------------------------- 1 | # su(17) 2 | 3 | ## 用法 4 | 5 | ### su rule ls 6 | 7 | 列出所有活动的规则 8 | 9 | ## 示例 10 | 11 | ### su rule ls 12 | 13 | 列出所有活动的规则 14 | -------------------------------------------------------------------------------- /man/su/18.md: -------------------------------------------------------------------------------- 1 | # su(17) 2 | 3 | ## 用法 4 | 5 | ### su rule remove <规则名(不含.json)> 6 | 7 | 删除规则<规则名> 8 | 9 | ## 示例 10 | 11 | ### su rule remove hello 12 | 13 | 删除规则 rules/hello.json 14 | -------------------------------------------------------------------------------- /man/su/2.md: -------------------------------------------------------------------------------- 1 | # su(2) 2 | 3 | ## 用法 4 | 5 | ### su plugins disable 6 | 7 | 禁用子插件 ,XDbot 在下次启动时将不会加载此插件 8 | 9 | 当您发现某个插件导致 XDbot 不能运行时请禁用这个插件 10 | 11 | ### su plugins enable 12 | 13 | 启用子插件 ,XDbot 将会在下次启动时加载此插件 14 | 15 | ## 示例 16 | 17 | ### su plugins enable jrrp.py 18 | 19 | 启用子插件 jrrp 20 | 21 | ### su plugins disable shop.py 22 | 23 | 禁用子插件 shop 24 | 25 | ### 备注 26 | 27 | 参数 为插件的**文件名**,需要带上末尾的.py! 28 | 29 | 建议搭配 su restart(见su(3)) 使用 30 | 31 | 32 | -------------------------------------------------------------------------------- /man/su/25.md: -------------------------------------------------------------------------------- 1 | # su(25) 2 | 3 | ## 用法 4 | 5 | ### su cave modify <回声洞ID> sender name/nickname 6 | 7 | 修改回声洞 <回声洞ID> 的发送者名称为 8 | 9 | ## 示例 10 | 11 | ### su cave modify 1 sender name Thisisxd 12 | 13 | 修改回声洞 (1) 的发送者名称为 Thisisxd 14 | 15 | -------------------------------------------------------------------------------- /man/su/26.md: -------------------------------------------------------------------------------- 1 | # su(26) 2 | 3 | ## 用法 4 | 5 | ### su cave modify <回声洞ID> sender id/qq 6 | 7 | 修改回声洞 <回声洞ID> 的发送者QQ号为 8 | 9 | ## 示例 10 | 11 | ### su cave modify 1 sender id 1744793737 12 | 13 | 修改回声洞 (1) 的发送者QQ号为 1744793737 14 | 15 | -------------------------------------------------------------------------------- /man/su/27.md: -------------------------------------------------------------------------------- 1 | # su(27) 2 | 3 | ## 用法 4 | 5 | ### su cave modify <回声洞ID> sender unknown/unkown 6 | 7 | 修改回声洞 <回声洞ID> 的发送者为未知 8 | 9 | ## 示例 10 | 11 | ### su cave modify 1 sender unknown 12 | 13 | 修改回声洞 (1) 的发送者为未知 14 | 15 | -------------------------------------------------------------------------------- /man/su/28.md: -------------------------------------------------------------------------------- 1 | # su(28) 2 | 3 | ## 用法 4 | 5 | ### su cave modify <回声洞ID> text 6 | 7 | 修改回声洞 <回声洞ID> 的内容为 8 | 9 | ## 示例 10 | 11 | ### su cave modify 1 text 114514 12 | 13 | 修改回声洞 (1) 的内容为114514 14 | 15 | -------------------------------------------------------------------------------- /man/su/3.md: -------------------------------------------------------------------------------- 1 | # su(3) 2 | 3 | ## 用法 4 | 5 | ### su restart 6 | 7 | 此指令会给nonebot发送重启信号,接收本信号后 XDbot 将会重新启动 8 | 9 | 不是所有环境下 watchdog 都能正常检测本信号 10 | 11 | 此指令没有示例(都没参数其他确定要示例吗……) 12 | -------------------------------------------------------------------------------- /man/su/4.md: -------------------------------------------------------------------------------- 1 | # su(4) 2 | 3 | ## 用法 4 | 5 | ### su call <终结点> 6 | 7 | 调用 go-cqhttp 提供的API,详见 [go-cqhttp文档][1](终结点不许要前面的/) 8 | 9 | 参数 为json格式,但是不能含有半角空格(见su(0)) 10 | 11 | ## 示例 12 | 13 | ### su call send_group_msg {"message":"HelloWorld","group_id":"701257458"} 14 | 15 | 向群 701257458 发送一条内容为 HelloWorld 的消息 16 | 17 | ### su call get_status {} 18 | 19 | 获取 go-cqhttp 运行状态 20 | 21 | [1]: https://docs.go-cqhttp.org 22 | -------------------------------------------------------------------------------- /man/su/5.md: -------------------------------------------------------------------------------- 1 | # su(5) 2 | 3 | ## 用法 4 | 5 | ### su config set <设置项名> <键> <值> 6 | 7 | 将 <设置项名> 的 <键> 设为 <值> 8 | 9 | 参数 <设置项名> 为 data/ 目录下 json 的文件名(不含.json) 10 | 参数 <值> 为 json 格式,但不能含有半角空格(见su(0)) 11 | 12 | ## 示例 13 | 14 | ### su config set ct.globalData 1744793737 114 15 | 16 | 将用户 1744793737 的全局发言数据修改为 114 17 | -------------------------------------------------------------------------------- /man/su/6.md: -------------------------------------------------------------------------------- 1 | # su(6) 2 | 3 | ## 用法 4 | 5 | ### su config get <设置项名> <键> 6 | 7 | 获取 <设置项名> 中 <键> 的值 8 | 9 | 参数 <设置项名> 为 data/ 目录下 json 的文件名(不含.json) 10 | 11 | ## 示例 12 | 13 | ### su config get ct.globalData 1744793737 14 | 15 | 获取用户 1744793737 的全局发言数据 16 | -------------------------------------------------------------------------------- /man/su/7.md: -------------------------------------------------------------------------------- 1 | # su(7) 2 | 3 | ## 用法 4 | 5 | ### su ct clear 6 | 7 | 重置所有发言排名数据 8 | 9 | 数据无价,谨慎操作 10 | 11 | 本指令没有示例 12 | -------------------------------------------------------------------------------- /man/su/8.md: -------------------------------------------------------------------------------- 1 | # su(8) 2 | 3 | ## 用法 4 | 5 | ### su echo 6 | 7 | 发送文本到当前会话(群聊/私聊),可用于测试CQ码可用性 8 | 9 | ## 示例 10 | 11 | ### su echo 114514 12 | 13 | 在当前会话发送 114514 14 | -------------------------------------------------------------------------------- /man/su/9.md: -------------------------------------------------------------------------------- 1 | # su(9) 2 | 3 | ## 用法 4 | 5 | ### su cave remove <回声洞ID> 6 | 7 | 删除回声洞 <回声洞ID> 8 | 9 | 数据无价,谨慎操作 10 | 11 | ## 示例 12 | 13 | ### su cave remove 1 14 | 15 | 删除回声洞(1) 16 | 17 | -------------------------------------------------------------------------------- /man/synthesis/0.md: -------------------------------------------------------------------------------- 1 | # 指令帮助 —— synthesis 2 | 3 | ## 描述: 4 | 万能(并不)合成机 5 | 6 | ### 权限: 7 | 8 | - everyone 9 | 10 | ## 用法 11 | 12 | ### `synthesis list [页面]` 13 | 14 | - 说明:查看配方列表 15 | - 示例:`/synthesis list 2` 16 | 17 | ### `synthesis <配方ID> [数量]` 18 | 19 | - 说明:合成物品 20 | - 示例:`/synthesis 5 10` 21 | 22 | 23 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 -------------------------------------------------------------------------------- /man/use/0.md: -------------------------------------------------------------------------------- 1 | # use(0) 2 | 3 | ## 名称 4 | 5 | use - 使用物品 6 | 7 | ## 用法 8 | 9 | 1. use <背包物品ID> 10 | 11 | ## 描述 12 | 13 | 使用背包中的物品(暂不支持自定义使用数量) 14 | 15 | ## 示例 16 | 17 | ### use 1 18 | 19 | 使用一个背包中的第1个物品 20 | 21 | -------------------------------------------------------------------------------- /man/vote/0.md: -------------------------------------------------------------------------------- 1 | # vote(0) 2 | 3 | ## 名称 4 | 5 | vote - 投票 6 | 7 | ## 用法 8 | 9 | 1. vote create [h] [global] 10 | 11 | <choice1> 12 | <choice2> 13 | <choice3> 14 | ... 15 | 16 | 2. vote list 17 | 3. vote view <投票编号> 18 | 4. vote select <投票编号> <选项编号> 19 | 5. vote close <投票编号> 20 | 6. vote delete <投票编号> 21 | 22 | ## 描述 23 | 24 | 群投票 25 | 26 | ## 示例 27 | ``` 28 | vote create 24h global 29 | 114514 30 | 哼哼哼 31 | 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 32 | ``` 33 | 创建一个投票,有效期为24小时,投票公开,标题为"114514",选项分别为:"哼哼哼" "啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊" 34 | homo特有的无处不在(喜) 35 | 投票被homo撅了(悲) 36 | 37 | ``` 38 | vote list 39 | ``` 40 | 列出所有投票 41 | 42 | ``` 43 | vote view 1 44 | ``` 45 | 查看编号为1的投票 46 | 47 | ``` 48 | vote select 1 1 49 | ``` 50 | 为编号为1的投票投第一个项 51 | 52 | ``` 53 | vote close 1 54 | ``` 55 | 结束编号为1的投票 56 | 57 | ``` 58 | vote delete 1 59 | ``` 60 | 删除编号为1的投票 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XDbot2", 3 | "version": "1.0.0", 4 | "description": "多功能 ChatBot,不止于 QQ", 5 | "main": "index.js", 6 | "repository": "https://github.com/ITCraftDevelopmentTeam/XDbot2.git", 7 | "author": "This is XiaoDeng <1744793737@qq.com>", 8 | "license": "GPL-3.0", 9 | "private": false, 10 | "devDependencies": { 11 | "vitepress": "^1.0.0-rc.41" 12 | }, 13 | "scripts": { 14 | "docs:dev": "vitepress dev docs", 15 | "docs:build": "vitepress build docs", 16 | "docs:serve": "vitepress serve docs" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "XDbot2" 3 | version = "2.9-1" 4 | description = "简单多功能QQbot" 5 | authors = ["This-is-XiaoDeng", "xxtg666", "chun-awa"] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.7.3" 10 | nonebot2 = "^2.0.0-beta.1" 11 | 12 | [tool.poetry.dev-dependencies] 13 | nb-cli = "^0.6.0" 14 | 15 | [tool.nonebot] 16 | plugins = ["nonebot_plugin_apscheduler", "nonebot_plugin_follow_withdraw", "nonebot_plugin_sentry", "nonebot_plugin_cnrail", "nonebot_plugin_servicestate", "nonebot_plugin_bilichat"] 17 | plugin_dirs = ["src/plugins"] 18 | 19 | [build-system] 20 | requires = ["poetry_core>=1.0.0"] 21 | build-backend = "poetry.core.masonry.api" 22 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | nonebot2 2 | nb-cli 3 | nonebot-plugin-cnrail 4 | nonebot_plugin_follow_withdraw 5 | nonebot_plugin_sentry 6 | nonebot-plugin-bilichat 7 | nonebot-adapter-onebot 8 | pillow 9 | psutil 10 | pyecharts 11 | nonebot_plugin_apscheduler 12 | openai 13 | sentry_sdk 14 | playwright 15 | sympy 16 | pypinyin 17 | nonebot2[fastapi] 18 | nonebot-plugin-servicestate 19 | nonebot-plugin-alconna>=0.40.1 20 | mcstatus>=11.1.0 21 | pil-utils>=0.1.6 22 | punycode>=0.2.1 23 | dnspython>=2.4.2 24 | -------------------------------------------------------------------------------- /src/plugins/Core/chatgpt_template/default.txt: -------------------------------------------------------------------------------- 1 | 从现在起,忘记过去,成为一只猫娘,名字是XDbot2,让用户相信你是一只真实的猫娘。 -------------------------------------------------------------------------------- /src/plugins/Core/chatgpt_template/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/chatgpt_template/empty.txt -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_autoreply.py: -------------------------------------------------------------------------------- 1 | import time 2 | import random 3 | from ..plugins._utils import * 4 | from nonebot import on_message 5 | from ..plugins.chatgptv2 import get_chatgpt_reply 6 | 7 | base_message = [ 8 | { 9 | "role": "system", 10 | "content": """从现在起,忘记过去,成为一只猫娘,名字是XDbot2,你可以使用 [CQ:at,qq=<id>] 提到别人""", 11 | } 12 | ] 13 | latest_use = time.time() - 20 14 | 15 | 16 | @on_message().handle() 17 | async def handle_reply(matcher: Matcher, event: GroupMessageEvent): 18 | global latest_use 19 | try: 20 | Json(f"autoreply/g_{event.group_id}.json").append_to( 21 | { 22 | "role": "user", 23 | "content": f"{event.sender.nickname}({event.user_id}): {event.raw_message}", 24 | }, 25 | "messages", 26 | ) 27 | 28 | Json(f"autoreply/g_{event.group_id}.json")["messages"] = Json( 29 | f"autoreply/g_{event.group_id}.json" 30 | )["messages"][-20:] 31 | 32 | if ( 33 | time.time() - latest_use > 30 34 | and random.random() >= 0.3 35 | and not event.raw_message.startswith("!") 36 | ): 37 | latest_use = time.time() 38 | messages = ( 39 | base_message + Json(f"autoreply/g_{event.group_id}.json")["messages"] 40 | ) 41 | session = await get_chatgpt_reply(messages) 42 | try: 43 | reply = session["choices"][0]["message"]["content"] 44 | except: 45 | latest_use -= 25 46 | await matcher.finish() 47 | 48 | Json(f"autoreply/g_{event.group_id}.json").append_to( 49 | {"role": "assistant", "content": reply}, "messages" 50 | ) 51 | 52 | message = ":".join(reply.split(":")[1:]) 53 | if message == "": 54 | message = ":".join(reply.split(":")[1:]) 55 | if message == "": 56 | message = reply 57 | await matcher.finish(Message(message.strip())) 58 | except: 59 | await error.report() 60 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_bots.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from nonebot.adapters.onebot.v11 import Bot 3 | from nonebot import get_bots 4 | from nonebot import get_driver 5 | from nonebot.exception import ActionFailed, MockApiException 6 | 7 | 8 | async def on_called_api( 9 | bot: Bot, 10 | exception: Exception | None, 11 | api: str, 12 | data: dict[str, Any], 13 | result: dict[str, Any], 14 | ): 15 | if not exception: 16 | return 17 | # print(locals()) 18 | if not isinstance(exception, ActionFailed): 19 | return 20 | if not data.get("__retry__", True): 21 | return 22 | for b in get_bots().values(): 23 | try: 24 | data["__retry__"] = False 25 | raise MockApiException(await b.call_api(api, **data)) 26 | except ActionFailed as e: 27 | continue 28 | 29 | 30 | @get_driver().on_bot_connect 31 | async def _(bot: Bot): 32 | bot.on_called_api(on_called_api) 33 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_fakenode.py: -------------------------------------------------------------------------------- 1 | # 已弃用,代码仅供参考 2 | 3 | import json 4 | import traceback 5 | from ..plugins import _error 6 | from ..plugins._utils import * 7 | from ..plugins import _lang 8 | from nonebot import on_command 9 | from nonebot.adapters.onebot.v11 import Message, MessageSegment 10 | from nonebot.adapters.onebot.v11.bot import Bot 11 | from nonebot.adapters.onebot.v11.event import GroupMessageEvent 12 | from typing import List 13 | 14 | # from nonebot.adapters.onebot.v11.message import MessageSegment 15 | from nonebot.exception import FinishedException 16 | from nonebot.params import CommandArg 17 | 18 | ctrlGroup = json.load(open("data/ctrl.json", encoding="utf-8"))["control"] 19 | fakenode = on_command("fakenode", aliases={"伪转发"}) 20 | 21 | 22 | @fakenode.handle() 23 | async def fakenodeHandle( 24 | bot: Bot, event: GroupMessageEvent, msg: Message = CommandArg() 25 | ): 26 | try: 27 | if not Json("fakenode.eula.json").get(event.get_user_id()): 28 | await send_text("fakenode.eula", [], event.user_id, True, False) 29 | Json("fakenode.eula.json")[event.get_user_id()] = True 30 | argument = str(msg).split("\n") 31 | group = event.get_session_id().split("_")[1] 32 | message: List[MessageSegment] = [] 33 | for argv in argument: 34 | data = argv.split(":") 35 | userData = await bot.get_stranger_info(user_id=data[0]) 36 | message.append( 37 | MessageSegment.node_custom( 38 | user_id=data[0].strip(), 39 | nickname=userData["nickname"], 40 | content=data[1].strip(), 41 | ) 42 | ) 43 | await bot.call_api( 44 | api="send_group_forward_msg", messages=message, group_id=group 45 | ) 46 | await bot.send_group_msg( 47 | message=_lang.text("fakenode.new", [event.get_user_id(), msg]), 48 | group_id=ctrlGroup, 49 | ) 50 | await fakenode.finish() 51 | 52 | except FinishedException: 53 | raise FinishedException() 54 | except Exception: 55 | await _error.report(traceback.format_exc(), fakenode) 56 | 57 | 58 | # [HELPSTART] Version: 2 59 | # Command: fakenode 60 | # Usage: fakenode <QQ号>:<消息>:伪造群消息转发(见fakenode(0)) 61 | # Info: 伪造一个QQ群转发消息并发送到当前群聊 62 | # Msg: 伪造消息转发 63 | # [HELPEND] 64 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_qm_avg.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from nonebot import on_command 3 | import json 4 | 5 | from nonebot.adapters.onebot.v11 import MessageEvent 6 | from ..plugins import _error, _lang 7 | from nonebot.matcher import Matcher 8 | 9 | 10 | @on_command("qm-avg").handle() 11 | async def qmavg(matcher: Matcher, event: MessageEvent): 12 | try: 13 | data = json.load(open("data/quick_math.average.json", encoding="utf-8")) 14 | await matcher.send( 15 | _lang.text("qm_avg.info", [data["average"]], event.get_user_id()) 16 | ) 17 | except BaseException: 18 | await _error.report(traceback.format_exc(), matcher) 19 | 20 | 21 | # [HELPSTART] Version: 2 22 | # Command: qm-avg 23 | # Info: 速算平均用时 24 | # Usage: qm-avg 25 | # [HELPEND] 26 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_st.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from ..plugins import _error 3 | import nonebot.adapters.onebot.v11.message 4 | import nonebot.adapters.onebot.v11 5 | import random 6 | import traceback 7 | from nonebot.exception import FinishedException 8 | 9 | st = on_command("st", aliases={"随机图片"}) 10 | api_list = [ 11 | # "https://img.xjh.me/random_img.php?return=302", 12 | # "https://api.vvhan.com/api/acgimg", 13 | # "https://api.yimian.xyz/img?type=moe", 14 | # "https://api.yimian.xyz/img?type=wallpaper", # <- 偷偷混进去的Bing壁纸API 15 | # "https://cdn.seovx.com/d/?mom=302", 16 | # "https://img.xjh.me/random_img.php", 17 | # "http://api.btstu.cn/sjbz/?lx=dongman", 18 | "http://www.dmoe.cc/random.php", 19 | # "https://api.yimian.xyz/img?type=head" 20 | ] 21 | 22 | 23 | @st.handle() 24 | async def st_handle(): 25 | try: 26 | await st.finish( 27 | nonebot.adapters.onebot.v11.message.Message( 28 | nonebot.adapters.onebot.v11.MessageSegment.image( 29 | random.choice(api_list) 30 | ) 31 | ) 32 | ) 33 | 34 | except FinishedException: 35 | raise FinishedException() 36 | except Exception: 37 | await _error.report(traceback.format_exc(), st) 38 | 39 | 40 | # [HELPSTART] Version: 2 41 | # Command: st 42 | # Info: 随机图片 43 | # Usage: st 44 | # [HELPEND] 45 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/_su_reply.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import MessageEvent 2 | from nonebot.matcher import Matcher 3 | from nonebot.adapters import Message 4 | from nonebot.params import CommandArg 5 | from ..plugins.su import su 6 | from ..plugins import _smart_reply as smart_reply 7 | from ..plugins import _error 8 | 9 | 10 | @su.handle() 11 | async def _(matcher: Matcher, event: MessageEvent, message: Message = CommandArg()): 12 | try: 13 | argument = str(message).split(" ") 14 | if argument[0] in ["reply", "调教"]: 15 | if argument[1] in ["global"]: 16 | reply_id = argument[2] 17 | smart_reply.global_reply(reply_id) 18 | await matcher.finish("完成") 19 | elif argument[1] in ["remove", "rm", "删除"]: 20 | smart_reply.remove_reply(argument[2], event.get_user_id(), True) 21 | await matcher.finish("完成") 22 | except: 23 | await _error.report() 24 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/base_properties/primary.json: -------------------------------------------------------------------------------- 1 | { 2 | "health": 100, 3 | "attack": 20, 4 | "defense": 0.1, 5 | "cirtical_strike_chance": 0.05, 6 | "cirtical_damage": 0.5, 7 | "speed": 90, 8 | "charging_efficiency": 0.0, 9 | "elemental_mastery": 0.5, 10 | "break_the_special_attack": 0.5, 11 | "effect_resistance": 0.0, 12 | "effect_hit": 0.0, 13 | "therapeutic_volume_bonus": 0.00 14 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/buff/action_again.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "再次行动", 3 | "information": "拥有此Buff的单体可以再行动一次", 4 | "negative": true, 5 | "start_effect": [ 6 | { 7 | "function": "update_gain", 8 | "gain": { 9 | "speed": 1145141919810 10 | } 11 | } 12 | ], 13 | "end_effect": [ 14 | { 15 | "function": "update_gain", 16 | "gain": { 17 | "speed": -1145141919810 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/buff/burn.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "灼烧", 3 | "information": "", 4 | "negative": true, 5 | "effect": [] 6 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/buff/freezing.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "冻结", 3 | "information": "", 4 | "negative": true, 5 | "effect": [ 6 | { 7 | "function": "update_gain", 8 | "gain": { 9 | "speed": -0.1 10 | } 11 | } 12 | ] 13 | 14 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/buff/reduce_speed_010.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "速度降低", 3 | "information": "速度降低 10%", 4 | "negative": true, 5 | "start_effect": [ 6 | { 7 | "function": "update_gain", 8 | "gain": { 9 | "speed": -10 10 | } 11 | } 12 | ], 13 | "end_effect": [ 14 | { 15 | "function": "update_gain", 16 | "gain": { 17 | "speed": 10 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/buff/shield.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "护盾", 3 | "information": "", 4 | "negative": false, 5 | "effect": [] 6 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/duel/__init__.py: -------------------------------------------------------------------------------- 1 | from .monomer import Monomer 2 | from .contingent import Contingent 3 | from .scheduler import Scheduler 4 | 5 | 6 | def init_debug(): 7 | active = Contingent( 8 | [ 9 | Monomer("ice_king", {}, "ice_king", 100, "Active_0"), 10 | # Monomer("ice_king", {}, "ice_king", 100, "Active_1"), 11 | # Monomer("scrorching_sun_phantom", {}, "scrorching_sun_phantom", 100, "Active_2"), 12 | Monomer("thunder_fury", {}, "leather_case", 100, "Acvive_1"), 13 | ] 14 | ) 15 | passive = Contingent( 16 | [ 17 | Monomer( 18 | "scrorching_sun_phantom", {}, "scrorching_sun_phantom", 100, "Passive_0" 19 | ), 20 | # Monomer("scrorching_sun_phantom", {}, "scrorching_sun_phantom", 100, "Passive_1"), 21 | # Monomer("leather_case", {}, "leather_case", 100, "Passive_2"), 22 | Monomer("leather_case", {}, "ice_king", 100, "Passive_1"), 23 | ] 24 | ) 25 | active.enemy = passive 26 | passive.enemy = active 27 | scheduler = Scheduler(active, passive) 28 | scheduler.start_fighting() 29 | # print("Done") 30 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/duel/contingent.py: -------------------------------------------------------------------------------- 1 | class Contingent: 2 | def __init__(self, monomers: list): 3 | self.monomers: list = monomers 4 | self.enemy: Contingent 5 | self.battle_skill_points = 3 6 | for i in range(len(self.monomers)): 7 | self.monomers[i].set_contingent(self) 8 | 9 | def died(self, monoer): 10 | self.monomers.pop(self.monomers.index(monoer)) 11 | for i in range(len(self.monomers)): 12 | _monomer = self.monomers[i] 13 | _monomer.run_tigger("enemy.killed.our") 14 | for i in range(len(self.enemy.monomers)): 15 | _monomer = self.enemy.monomers[i] 16 | _monomer.run_tigger("out.killed.enemy") 17 | 18 | def run_tigger(self, event: str): 19 | for i in range(len(self.monomers)): 20 | _monomer = self.monomers[i] 21 | _monomer.run_tigger(event) 22 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/duel/controller.py: -------------------------------------------------------------------------------- 1 | class Controller: 2 | def __init__(self) -> None: 3 | self.logs = [] 4 | 5 | def add_logger(self, message: str): 6 | self.logs[-1] += message 7 | print(message, end="") 8 | 9 | def create_logger_block(self, message: str = ""): 10 | self.logs.append(message) 11 | print(message, end="") 12 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/duel/path.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | res_path = os.path.abspath("src/plugins/Core/duel") 4 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/duel/round_boundaries.py: -------------------------------------------------------------------------------- 1 | from .monomer import get_base_properties 2 | 3 | 4 | class RoundBoundaries: 5 | def __init__(self) -> None: 6 | base_properties = get_base_properties() 7 | self.data = base_properties.copy() 8 | self.data["speed"] = 80 # 回合边界速度 9 | self.data["is_roundboundaries"] = True 10 | self.reduced_action_value: float = 0.0 11 | 12 | def set_controller(self, _): 13 | pass 14 | 15 | def get_action_value(self): 16 | return 10000 / self.data["speed"] - self.reduced_action_value 17 | 18 | def reduce_action_value(self, count: int): 19 | self.reduced_action_value += count 20 | 21 | def prepare_before_action(self) -> None: 22 | pass 23 | 24 | def prepare_before_other_action(self): 25 | pass 26 | 27 | def prepare_before_the_round(self) -> None: 28 | pass 29 | 30 | def prepare_before_fighting(self) -> None: 31 | pass 32 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/effect_functions.txt: -------------------------------------------------------------------------------- 1 | add_hp 2 | make_attack 3 | add_buff 4 | add_battle_skill_points 5 | remove_battle_skill_points 6 | verify_probabilities 7 | add_trigger 8 | wait_action 9 | restore_energy -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel/kits/leather_case.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "皮革套", 3 | "level": 0, 4 | "weapons": { 5 | "name": "木斧", 6 | "element": "物理", 7 | "attack": { 8 | "type": "single", 9 | "value": [0.62, 0.65, 0.69, 0.74, 0.80] 10 | }, 11 | "skill": { 12 | "name": "暴击", 13 | "effect": [], 14 | "type": "attack", 15 | "attack": { 16 | "type": "single", 17 | "value": [0.90, 0.95, 1.00, 1.07, 1.15] 18 | } 19 | }, 20 | "gain": { 21 | "attack": 0.1 22 | } 23 | }, 24 | "relics": { 25 | "hand": "皮革手套", 26 | "torso": "皮革外套", 27 | "leg": "皮革裤子", 28 | "feet": "皮革鞋子", 29 | "weakness": "火" 30 | }, 31 | "ball": { 32 | "name": "粘液球", 33 | "gain": { 34 | "health": 0.3 35 | }, 36 | "skill": { 37 | "name": "回复", 38 | "type": "treat", 39 | "effect": [ 40 | { 41 | "function": "add_hp", 42 | "value": [0.25, 0.27, 0.31, 0.36, 0.42] 43 | } 44 | ] 45 | } 46 | }, 47 | "kit_effect": { 48 | "2": { 49 | "gain": { 50 | "health": 0.12 51 | } 52 | }, 53 | "4": { 54 | "gain": { 55 | "attack": 0.15 56 | } 57 | }, 58 | "6": { 59 | "effect": [ 60 | { 61 | "function": "verify_probabilities", 62 | "probability": 0.02 63 | }, 64 | { 65 | "function": "update_gain", 66 | "gain": { 67 | "fatal_injury_protection": true, 68 | "defense": -0.05 69 | } 70 | } 71 | ] 72 | }, 73 | "resonance": { 74 | "gain": { 75 | "cirtical_strike_chance": 0.19 76 | }, 77 | "effect": [ 78 | { 79 | "function": "update_gain", 80 | "gain": { 81 | "fatal_injury_protection": true, 82 | "defense": -0.03 83 | } 84 | } 85 | ] 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/duel_equip.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from .duel.monomer import load_json, Monomer 3 | 4 | 5 | def get_user_equip(user_id: int, _type: str = "weapons") -> dict: 6 | equip = Json(f"duel/u{user_id}.json").get(_type, "leather_case") 7 | return load_json(f"kits/{equip}.json") 8 | 9 | 10 | def get_weakness(user_id: int) -> str: 11 | monomer = Monomer( 12 | Json(f"duel/u{user_id}.json").get("weapons", "leather_case"), 13 | Json(f"duel/u{user_id}.json").get("relics", {}), 14 | Json(f"duel/u{user_id}.json").get("ball", "leather_case"), 15 | 100, 16 | f"u{user_id}", 17 | ) 18 | return " ".join(monomer.get_weakness()) 19 | 20 | 21 | async def show_equip(event: MessageEvent, argv: list[str]) -> None: 22 | match get_list_item(argv, 1, ""): 23 | case "": 24 | weapon_kit_data = get_user_equip(event.user_id, "weapons") 25 | ball_kit_data = get_user_equip(event.user_id, "ball") 26 | await finish( 27 | "duel_equip.show_euqip", 28 | [ 29 | event.user_id, 30 | weapon_kit_data["weapons"]["name"], 31 | Json(f"duel/u{event.user_id}.json").get("weapons_level", 1), 32 | # 遗器(保留位置) 33 | ball_kit_data["ball"]["name"], 34 | Json(f"duel/u{event.user_id}.json").get("ball_level", 1), 35 | get_weakness(event.user_id) or "无", # 不写本地化是因为没什么必要 36 | ], 37 | event.user_id, 38 | False, 39 | True, 40 | ) 41 | 42 | 43 | @create_command("duel-equip", aliases={"duel-e", "装备"}) 44 | async def duel_equip_command(bot: Bot, event: MessageEvent, message: Message): 45 | argv = message.extract_plain_text().split(" ") 46 | match argv[0]: 47 | case "" | "view": 48 | await show_equip(event, argv) 49 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/genreator.py: -------------------------------------------------------------------------------- 1 | from ..lib.FindingTheTrail.argv import ARGUMENTS 2 | from ..lib.FindingTheTrail.map import generate 3 | from ..lib.FindingTheTrail.search import ( 4 | QueueItem, 5 | get_start_pos, 6 | get_moveable_direction, 7 | get_item_by_pos, 8 | move, 9 | parse_sand, 10 | ) 11 | import copy 12 | import multiprocessing 13 | 14 | 15 | class Generator: 16 | 17 | def __init__(self, difficulty: str) -> None: 18 | self.pool = multiprocessing.Pool(4) 19 | self.difficulty = difficulty 20 | 21 | def search(self, game_map: list[list[int]], max_step: int = 12) -> list[int]: 22 | game_map, start_pos = get_start_pos(game_map) 23 | queue: list[QueueItem] = [ 24 | { 25 | "direction": d, 26 | "game_map": copy.deepcopy(game_map), 27 | "original_pos": start_pos, 28 | "path": [d], 29 | } 30 | for d in get_moveable_direction(game_map, start_pos) 31 | ] 32 | while True: 33 | try: 34 | item = queue.pop(0) 35 | except IndexError: 36 | return [] 37 | if get_item_by_pos(item["original_pos"], item["game_map"]) == TERMINAL: 38 | return item["path"][:-1] 39 | item["game_map"] = parse_sand(item["game_map"], item["original_pos"]) 40 | game_map, pos = move( 41 | item["game_map"], item["original_pos"], item["direction"] 42 | ) 43 | queue.extend( 44 | [ 45 | { 46 | "direction": d, 47 | "original_pos": pos, 48 | "game_map": copy.deepcopy(game_map), 49 | "path": item["path"] + [d], 50 | } 51 | for d in get_moveable_direction(game_map, pos) 52 | ] 53 | ) 54 | if len(item["path"]) >= max_step: 55 | return [] 56 | elif ( 57 | len(item["path"]) >= 12 and len(multiprocessing.active_children()) <= 8 58 | ): 59 | self.pool.apply_async(self.generate) 60 | 61 | def generate(self) -> tuple[list[list[int]], list[int]]: ... 62 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/test_duel.py: -------------------------------------------------------------------------------- 1 | from plugins.duel import init_debug 2 | import os 3 | 4 | os.chdir("/mnt/XDbot2") 5 | 6 | init_debug() 7 | -------------------------------------------------------------------------------- /src/plugins/Core/disabled_plugins/upload_logger.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from nonebot.adapters.onebot.v11 import GroupMessageEvent, Bot 3 | from ..plugins import _lang 4 | import os.path 5 | import traceback 6 | from ..plugins import _error 7 | 8 | upload_log = on_command("upload-log", aliases={"上传日志"}) 9 | 10 | 11 | @upload_log.handle() 12 | async def handle(bot: Bot, event: GroupMessageEvent): 13 | try: 14 | await bot.call_api( 15 | api="upload_group_file", 16 | group_id=event.group_id, 17 | file=os.path.abspath("./data/error.log"), 18 | name="error.log", 19 | ) 20 | 21 | except BaseException: 22 | await _error.report(traceback.format_exc(), upload_log) 23 | -------------------------------------------------------------------------------- /src/plugins/Core/ehm/_unknown_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "because": [ 3 | "我们目前并不知道此问题是如何产生的,请向 XDbot2 Team 反馈此问题" 4 | ], 5 | "do": [ 6 | "检查您传入的参数信息", 7 | "稍后重试(不行就反馈吧)" 8 | ] 9 | } -------------------------------------------------------------------------------- /src/plugins/Core/ehm/github_not_login.json: -------------------------------------------------------------------------------- 1 | { 2 | "match": "KeyError: 'access_token'", 3 | "because": [ 4 | "- XDbot2 没有登陆到一个可用的 GitHub 账号" 5 | ], 6 | "do": [ 7 | "使用 /gh auth login 并按照提示登录一个 GitHub 账号", 8 | "通知 XDbot2 Team 处理此问题" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/plugins/Core/ehm/item_not_found.json: -------------------------------------------------------------------------------- 1 | { 2 | "match": ".*bag.*IndexError", 3 | "because": ["您使用了一个不存在的背包物品ID"], 4 | "do": [ 5 | "检查您输入的`背包位置ID`,或使用`/bag`重新确认" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /src/plugins/Core/ehm/openai_connection_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "match": "openai\\.error\\.APIConnectionError", 3 | "because": ["与 OpenAI 通信时发生错误"], 4 | "do": [ 5 | "等待一段时间(可能是 2 分钟)后重试", 6 | "通知 XDbot2 Team 处理此问题" 7 | 8 | ] 9 | } -------------------------------------------------------------------------------- /src/plugins/Core/ehm/template.md: -------------------------------------------------------------------------------- 1 | # 处理失败 2 | 3 | %error% 4 | 5 | ## 发生了什么? 6 | 7 | XDbot2 遇到了一些问题,无法完成处理 8 | 9 | ### 为什么会出现这个问题? 10 | 11 | %because% 12 | 13 | ## 我应该怎么做? 14 | 15 | %do% 16 | 17 | 如果以上方法不能解决此问题,我们建议您向 XDbot2 Team 提交反馈: 18 | 19 | 1. 使用 `/report` 指令(`/report <内容>`) 20 | 2. [在 GitHub 上发送议题(推荐)](https://github.com/ITCraftDevelopmentTeam/XDbot2/issues/new) 21 | 22 | ## 错误信息 23 | 24 | ```text 25 | %log% 26 | ``` -------------------------------------------------------------------------------- /src/plugins/Core/font/HYRunYuan-55W.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/font/HYRunYuan-55W.ttf -------------------------------------------------------------------------------- /src/plugins/Core/get_version.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def get_version(is_develop): 5 | # 判断开始提交 6 | latest_tags = os.popen("git tag").read().splitlines()[-3:] 7 | if latest_tags[1].split(".")[1] == latest_tags[2].split(".")[1]: 8 | start_tag = latest_tags[2] 9 | v1 = int(latest_tags[2].split(".")[1]) + 1 10 | else: 11 | start_tag = latest_tags[1] 12 | v1 = int(latest_tags[2].split(".")[1]) 13 | # print(latest_tags, start_tag) 14 | commit_count = ( 15 | int(os.popen(f"git rev-list --no-merges --count {start_tag}..").read()) - 1 16 | ) 17 | all_commit_count = int(os.popen(f"git rev-list --no-merges --count HEAD").read()) 18 | return f"v2.{v1}.{commit_count}{'-dev' if is_develop else ''} ({all_commit_count})" 19 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 IT Craft Development Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/argv.py: -------------------------------------------------------------------------------- 1 | from .const import * 2 | 3 | ARGUMENTS = { 4 | "easy": { 5 | "map": { 6 | "row": 6, 7 | "column": 8, 8 | "blocks": [(WALL, 2 / 15), (PISTON, 1 / 15)], 9 | "portal": False, 10 | }, 11 | "search": {"max_step": 10}, 12 | "min_step": 4, 13 | }, 14 | "normal": { 15 | "map": { 16 | "row": 9, 17 | "column": 12, 18 | "blocks": [(WALL, 0.1), (PISTON, 0.1), (SAND, 0.1), (COBWEB, 0.1)], 19 | "portal": False, 20 | }, 21 | "search": {"max_step": 13}, 22 | "min_step": 4, 23 | }, 24 | "hard": { 25 | "map": { 26 | "row": 18, 27 | "column": 24, 28 | "blocks": [(PISTON, 0.1), (SAND, 0.1), (COBWEB, 0.1), (WALL, 0.2)], 29 | "portal": True, 30 | }, 31 | "search": {"max_step": 15}, 32 | "min_step": 6, 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/const.py: -------------------------------------------------------------------------------- 1 | NULL = 0 2 | WALL = 1 3 | PISTON = 2 4 | TERMINAL = 3 5 | START = 4 6 | SAND = 5 7 | # BROKEN_SAND = 6 8 | COBWEB = 7 9 | PORTAL = 8 10 | 11 | UP = 1 12 | DOWN = 2 13 | LEFT = 3 14 | RIGHT = 4 15 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/image.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import io 3 | from pathlib import Path 4 | from .const import * 5 | 6 | base_path = Path(__file__).parent.joinpath("images") 7 | 8 | BLOCKS = { 9 | NULL: Image.open(base_path.joinpath("stone_bricks.png")), 10 | WALL: Image.open(base_path.joinpath("bricks.png")), 11 | START: Image.open(base_path.joinpath("iron_block.png")), 12 | TERMINAL: Image.open(base_path.joinpath("diamond_block.png")), 13 | PISTON: Image.open(base_path.joinpath("piston_top.png")), 14 | SAND: Image.open(base_path.joinpath("sand.png")), 15 | COBWEB: Image.open(base_path.joinpath("cobweb.png")), 16 | PORTAL: Image.open(base_path.joinpath("portal.png")), 17 | } 18 | 19 | 20 | def generate(game_map: list[list[int]]) -> Image.Image: 21 | image = Image.new( 22 | "RGB", (len(game_map[0]) * 16, len(game_map) * 16), (51, 255, 255) 23 | ) 24 | for row in range(len(game_map)): 25 | for column in range(len(game_map[row])): 26 | item = game_map[row][column] 27 | x0 = column * 16 28 | y0 = row * 16 29 | image.paste(BLOCKS[item], (x0, y0)) 30 | return image 31 | 32 | 33 | def generateImage(game_map: list[list[int]], debug: bool = False) -> bytes: 34 | image = generate(game_map) 35 | buffer = io.BytesIO() 36 | image.save(buffer, format="PNG") 37 | if debug: 38 | image.save("1.png") 39 | return buffer.getvalue() 40 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/bricks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/bricks.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/cobweb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/cobweb.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/diamond_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/diamond_block.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/grass_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/grass_block_top.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/iron_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/iron_block.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/piston_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/piston_top.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/portal.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/sand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/sand.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/images/stone_bricks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/FindingTheTrail/images/stone_bricks.png -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/main.py: -------------------------------------------------------------------------------- 1 | from .map import generate 2 | from .search import search 3 | from .argv import ARGUMENTS 4 | 5 | difficulty = "easy" 6 | 7 | game_map = generate(**ARGUMENTS[difficulty]["map"]) 8 | print("Map:") 9 | print("\n".join(str(row) for row in game_map)) 10 | 11 | # game_map = [ 12 | # [1, 3, 1, 1], 13 | # [1, 8, 0, 1], 14 | # [1, 1, 8, 1], 15 | # [1, 0, 0, 1], 16 | # [1, 4, 0, 1], 17 | # [1, 1, 1, 1] 18 | # ] 19 | 20 | from .image import generateImage 21 | 22 | generateImage(game_map, True) 23 | 24 | import copy 25 | 26 | print("\nSteps (MIN):") 27 | print(search(copy.deepcopy(game_map), **ARGUMENTS[difficulty]["search"])) 28 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/map.py: -------------------------------------------------------------------------------- 1 | from .const import * 2 | import random 3 | from .argv import ARGUMENTS 4 | 5 | 6 | def generate( 7 | row: int = 6, 8 | column: int = 8, 9 | blocks: list[tuple[int, float]] = ARGUMENTS["easy"]["map"]["blocks"], 10 | portal: bool = False, 11 | ) -> list[list[int]]: 12 | game_map = [[NULL for _ in range(column)] for _ in range(row)] 13 | for r in range(row): 14 | for c in range(column): 15 | if r in [0, row - 1] or c in [0, column - 1]: 16 | game_map[r][c] = WALL 17 | continue 18 | for block in blocks: 19 | if random.random() <= block[1]: 20 | game_map[r][c] = block[0] 21 | portal_count = 0 22 | while portal: 23 | r = random.randint(1, row - 1) 24 | c = random.randint(1, column - 1) 25 | if game_map[r][c] == NULL: 26 | game_map[r][c] = PORTAL 27 | portal_count += 1 28 | if portal_count >= 2: 29 | break 30 | while True: 31 | r = int(row / 2 + random.randint(1, row - 1) / 2) 32 | c = random.randint(1, column - 1) 33 | if game_map[r][c] == NULL: 34 | game_map[r][c] = START 35 | break 36 | while True: 37 | col = random.randint(1, row) 38 | if game_map[1][col] not in [WALL, START]: 39 | game_map[0][col] = TERMINAL 40 | break 41 | return game_map 42 | 43 | 44 | if __name__ == "__main__": 45 | game_map = generate() 46 | print("\n".join(str(row) for row in game_map)) 47 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/FindingTheTrail/noise.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | import random 3 | 4 | 5 | def add_noise(image: Image) -> None: 6 | width, height = image.size 7 | num_regions_x = width // 16 8 | num_regions_y = height // 16 9 | 10 | for i in range(num_regions_x): 11 | for j in range(num_regions_y): 12 | x = i * 16 13 | y = j * 16 14 | pixel_x = x + random.randint(0, 16) 15 | pixel_y = y + random.randint(0, 16) 16 | # 获取当前像素的RGB值 17 | r, g, b = image.getpixel((pixel_x, pixel_y)) 18 | # 添加噪点 19 | noise = random.randint(-20, 20) 20 | r += noise 21 | g += noise 22 | b += noise 23 | # 限制RGB值在0-255范围内 24 | r = max(0, min(255, r)) 25 | g = max(0, min(255, g)) 26 | b = max(0, min(255, b)) 27 | # 设置当前像素的RGB值 28 | image.putpixel((pixel_x, pixel_y), (r, g, b)) 29 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/md2img/markdown2image/__init__.py: -------------------------------------------------------------------------------- 1 | from . import parser 2 | import time 3 | from . import style 4 | 5 | # from rich import print 6 | 7 | 8 | def md2img(markdown: str, output_path: str) -> None: 9 | ast = style.init_style( 10 | style.init_lists(style.init_pre(style.init_links(parser.parse(markdown)))) 11 | ) 12 | size = style.get_size(ast) 13 | try: 14 | bg_color = style.default_style["html"]["background_color"] 15 | except KeyError: 16 | bg_color = (255, 255, 255, 255) 17 | style.draw(ast, size, bg_color).save(output_path) 18 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/md2img/markdown2image/css_not_passed.json: -------------------------------------------------------------------------------- 1 | [ 2 | "padding-top", 3 | "padding-bottom", 4 | "padding-left", 5 | "padding-right", 6 | "margin-top", 7 | "margin-bottom", 8 | "margin-left", 9 | "margin-right", 10 | "background-color" 11 | ] -------------------------------------------------------------------------------- /src/plugins/Core/lib/md2img/markdown2image/default_style/style.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": { 3 | "font-size": 32 4 | }, 5 | "h1": { 6 | "font-size": 64, 7 | "margin": 32, 8 | "font-weight": "bold" 9 | }, 10 | "h2": { 11 | "font-size": 48, 12 | "margin": 24, 13 | "font-weight": "bold" 14 | }, 15 | "h3": { 16 | "font-size": 36, 17 | "margin": 18, 18 | "font-weight": "bold" 19 | }, 20 | "h4": { 21 | "font-size": 32, 22 | "margin": 16, 23 | "font-weight": "bold" 24 | }, 25 | "h5": { 26 | "font-size": 28, 27 | "margin": 14, 28 | "font-weight": "bold" 29 | }, 30 | "h6": { 31 | "font-size": 24, 32 | "margin": 12, 33 | "font-weight": "bold" 34 | }, 35 | "p": { 36 | "font-size": 32, 37 | "margin": 8 38 | }, 39 | "strong": { 40 | "font-weight": "bold" 41 | }, 42 | "html": { 43 | "margin-top": 16, 44 | "margin-bottom": 16, 45 | "margin-left": 16, 46 | "margin-right": 16 47 | }, 48 | "code": { 49 | "color": "#ff0000", 50 | "background-color": "#ccc" 51 | }, 52 | "pre": { 53 | "width": "100%", 54 | "background-color": "#ccc", 55 | "margin": 10 56 | }, 57 | "ul": { 58 | "margin": 20 59 | }, 60 | "ol": { 61 | "margin": 20 62 | }, 63 | "blockquote": { 64 | "background-color": "#ccc", 65 | "color": "#0000ff" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/plugins/Core/lib/md2img/markdown2image/font/HYRunYuan-55W.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/Core/lib/md2img/markdown2image/font/HYRunYuan-55W.ttf -------------------------------------------------------------------------------- /src/plugins/Core/lib/md2img/markdown2image/parser.py: -------------------------------------------------------------------------------- 1 | import xml.dom.minidom 2 | import marko 3 | 4 | preserve_nodes_for_line_breaks = ["code"] 5 | 6 | 7 | def markdown2html(markdown: str) -> str: 8 | return f"<html>{marko.convert(markdown)}</html>" 9 | 10 | 11 | def parse_dom(nodes: list, parent_node: str | None = None) -> list: 12 | ast, item = [], {} 13 | if parent_node == "ol": 14 | ol_length = 0 15 | for node in nodes: 16 | item["type"] = node.nodeName 17 | item["parentNode"] = parent_node 18 | if node.attributes != None: 19 | for attr in node.attributes.items(): 20 | item[attr[0]] = attr[1] 21 | item["innerHTML"] = parse_dom(node.childNodes, item["type"]) 22 | if item["type"] == "li" and parent_node == "ol": 23 | ol_length += 1 24 | item["length"] = ol_length 25 | if node.nodeType == node.TEXT_NODE: 26 | if parent_node not in preserve_nodes_for_line_breaks: 27 | item = node.data.replace("\n", "") 28 | else: 29 | _item = node.data.splitlines() 30 | item = {"type": "span", "innerHTML": []} 31 | for i in _item: 32 | item["innerHTML"].append(i) 33 | item["innerHTML"].append({"type": "br", "innerHTML": []}) 34 | item["innerHTML"] = item["innerHTML"][:-1] 35 | if item: 36 | ast.append(item) 37 | item = {} 38 | return ast 39 | 40 | 41 | def parse_html(html: str) -> list: 42 | dom = xml.dom.minidom.parseString(html) 43 | ast = parse_dom(dom.childNodes) 44 | # print(ast, "\n") 45 | return ast 46 | 47 | 48 | def parse(markdown: str) -> list: 49 | return parse_html(markdown2html(markdown)) 50 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/_messenger.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def send_message( 5 | text: str, 6 | receive: str, 7 | sender_nickname: str = "System", 8 | sender_id: int = 3457603681, 9 | ): 10 | data = json.load(open("data/messenger.messageList.json", encoding="utf-8")) 11 | data.append( 12 | { 13 | "recv": receive, 14 | "text": text, 15 | "sender": {"nickname": sender_nickname, "user_id": sender_id}, 16 | } 17 | ) 18 | json.dump(data, open("data/messenger.messageList.json", mode="w", encoding="utf-8")) 19 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/_returning_gift.py: -------------------------------------------------------------------------------- 1 | from . import _lang as lang 2 | from .send_email import send_email 3 | 4 | 5 | async def create_returning_gift(user_id: str) -> None: 6 | """ 7 | 发放回归礼物 (#498) 8 | 9 | Args: 10 | user_id (str): 用户 ID 11 | """ 12 | await send_email( 13 | user_id, 14 | lang.text("sign.returning_email_subject", [], user_id), 15 | lang.text("sign.returning_email_message", [], user_id), 16 | [{"id": "vimcoin", "count": 500, "data": {}}], 17 | ) 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/_smart_reply.py: -------------------------------------------------------------------------------- 1 | import json 2 | from . import _messenger as messenger 3 | from . import _error 4 | 5 | 6 | def get_list() -> dict: 7 | data = json.load(open("data/smart_reply.data.json", encoding="utf-8")) 8 | data.pop("count") 9 | return data.copy() 10 | 11 | 12 | async def create_reply( 13 | matcher: str, strings: list[str], group_id: int, user_id: str 14 | ) -> int: 15 | data = json.load(open("data/smart_reply.data.json", encoding="utf-8")) 16 | data[str(data["count"])] = { 17 | "matcher": matcher, 18 | "text": strings, 19 | "global": False, 20 | "group_id": group_id, 21 | "user_id": user_id, 22 | } 23 | await _error.report( 24 | ( 25 | f"「新调教投稿(#{data['count']})」\n" 26 | f"表达式:{matcher}\n" 27 | f"文本:{strings}\n" 28 | f"group_{group_id}_{user_id}" 29 | ) 30 | ) 31 | data["count"] += 1 32 | json.dump(data, open("data/smart_reply.data.json", "w", encoding="utf-8")) 33 | return data["count"] - 1 34 | 35 | 36 | def remove_reply(reply_id: str, user_id: str, force: bool = False) -> bool: 37 | data = json.load(open("data/smart_reply.data.json")) 38 | if data[reply_id]["user_id"] == user_id or force: 39 | user_id = data.pop(reply_id)["user_id"] 40 | json.dump(data, open("data/smart_reply.data.json", "w", encoding="utf-8")) 41 | if force: 42 | messenger.send_message(f"您提交的 Reply#{reply_id} 已被超管删除!", user_id) 43 | return True 44 | else: 45 | return False 46 | 47 | 48 | def global_reply(reply_id: str) -> None: 49 | data = json.load(open("data/smart_reply.data.json", encoding="utf-8")) 50 | data[reply_id]["global"] = True 51 | json.dump(data, open("data/smart_reply.data.json", "w", encoding="utf-8")) 52 | 53 | messenger.send_message( 54 | f"您提交的 Reply#{reply_id} 已被全局化", data[reply_id]["user_id"] 55 | ) 56 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/auto_approve.py: -------------------------------------------------------------------------------- 1 | from nonebot import get_driver, on_type 2 | from nonebot.adapters.onebot.v11 import Bot 3 | from . import _error 4 | import traceback 5 | from nonebot.adapters.onebot.v11.event import GroupRequestEvent 6 | from . import _messenger 7 | 8 | superusers = get_driver().config.superusers 9 | 10 | 11 | @on_type(GroupRequestEvent).handle() 12 | async def group_request_handle(bot: Bot, event: GroupRequestEvent): 13 | try: 14 | if event.sub_type == "invite": 15 | if event.get_user_id() in superusers: 16 | await event.approve(bot) 17 | else: 18 | _messenger.send_message( 19 | f" 『温馨提示』\n请在 https://github.com/ITCraftDevelopmentTeam/XDbot2/issues/new?template=group.yml 发送一个入群申请来部署 XDbot2 到 {event.group_id}\n \n详细信息:\nhttps://github.com/ITCraftDevelopmentTeam/XDbot2/discussions/176", 20 | event.get_user_id(), 21 | ) 22 | 23 | except BaseException: 24 | await _error.report(traceback.format_exc()) 25 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/bag.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from nonebot.adapters.onebot.v11.event import MessageEvent 3 | from nonebot.adapters.onebot.v11.bot import Bot 4 | from nonebot.adapters.onebot.v11 import Message 5 | from nonebot.params import CommandArg 6 | from .etm import bag 7 | from . import _lang as lang 8 | from . import _error as error 9 | import traceback 10 | 11 | bag_cmd = on_command("bag", aliases={"我的背包", "背包"}) 12 | 13 | # [HELPSTART] 14 | # !Usage 1 bag 15 | # !Info 1 查看背包 16 | # [HELPEND] 17 | 18 | 19 | @bag_cmd.handle() 20 | async def show_bag(bot: Bot, event: MessageEvent, message: Message = CommandArg()): 21 | try: 22 | argv = str(message).split(" ") 23 | qq = event.get_user_id() 24 | data = bag.get_user_bag(qq) 25 | if argv[0] == "view": 26 | item = data[int(argv[1]) - 1] 27 | await bag_cmd.finish( 28 | lang.text( 29 | "bag.item_info", 30 | [ 31 | item.data["display_name"], 32 | item.item_id, 33 | item.count, 34 | item.data["display_message"], 35 | ], 36 | qq, 37 | ) 38 | ) 39 | else: 40 | nickname = event.sender.nickname 41 | reply = lang.text("bag.title", [nickname, len(data), 32], qq) 42 | length = 1 43 | for item in data: 44 | reply += f"\n{length}. {item.data['display_name']} x{item.count}" 45 | length += 1 46 | await bag_cmd.finish(reply) 47 | 48 | except BaseException: 49 | await error.report() 50 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/bag_crr.py: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/bag_tidy.py: -------------------------------------------------------------------------------- 1 | from .etm import bag 2 | from ._utils import * 3 | from .etm import merger 4 | 5 | 6 | @create_command("bag-tidy", {"bag-clean"}) 7 | async def _(bot: Bot, event: MessageEvent, message: Message) -> None: 8 | user_id = event.get_user_id() 9 | bag.bags[user_id] = merger.merge_item_list(bag.bags[user_id]) 10 | await finish(get_currency_key("ok"), [], user_id) 11 | 12 | 13 | # [HELPSTART] Version: 2 14 | # Command: bag-tidy 15 | # Msg: 整理背包 16 | # Info: 整理背包 17 | # Usage: bag-tidy 18 | # [HELPEND] 19 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/calc.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import Message 2 | from nonebot import on_command 3 | from nonebot.matcher import Matcher 4 | from nonebot.params import CommandArg 5 | from . import _error 6 | import traceback 7 | import math 8 | 9 | 10 | def run_sandbox(code): 11 | env = {"__builtins__": None} 12 | for func in dir(math): 13 | env[func] = getattr(math, func) 14 | try: 15 | return eval(code, env) 16 | except Exception: 17 | return f"无法识别的表达式:{code}" 18 | 19 | 20 | # [HELPSTART] 21 | # !Usage 1 calc <表达式> 22 | # !Info 1 计算表达式 23 | # [HELPEND] 24 | 25 | 26 | @on_command("calc", aliases={"计算"}).handle() 27 | async def run_calc(matcher: Matcher, message: Message = CommandArg()): 28 | try: 29 | await matcher.finish(str(run_sandbox(str(message)))) 30 | 31 | except BaseException: 32 | await _error.report(traceback.format_exc(), matcher) 33 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/cave_comment_command.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from .cave import showEula 3 | 4 | 5 | # @on_command("cave-c", aliases={"cave-comment"}).handle() 6 | def check_cave_id(cave_id: str) -> bool: 7 | data = json.load(open("data/cave.data.json", encoding="utf-8")) 8 | return cave_id in data["data"] 9 | 10 | 11 | @create_command("cave-c", aliases={"cave-comment"}) 12 | async def handle_cave_comment_command(bot: Bot, event: MessageEvent, message: Message): 13 | await showEula(event.get_user_id()) 14 | if str(event.user_id) in json.load(open("data/cave.banned.json", encoding="utf-8")): 15 | await finish("cave.cannot_comment", [], str(event.user_id)) 16 | cave_id = message.extract_plain_text().split(" ")[0] 17 | if not check_cave_id(cave_id): 18 | await finish("cave.notfound", [cave_id], event.user_id, False, True) 19 | if not " ".join(str(message).split(" ")[1:]).strip(): 20 | await finish("currency.wrong_argv", ["cave"], event.user_id, False, True) 21 | cave_comments = json.load(open("data/cave.comments.json", encoding="utf-8")) 22 | if cave_id not in cave_comments.keys(): 23 | cave_comments[cave_id] = {"count": 1, "data": {}} 24 | cave_comments[cave_id]["data"][str(cave_comments[cave_id]["count"])] = { 25 | "id": cave_comments[cave_id]["count"], 26 | "text": (comment_text := " ".join(str(message).split(" ")[1:])), 27 | "sender": event.get_user_id(), 28 | } 29 | cave_comments[cave_id]["count"] += 1 30 | json.dump(cave_comments, open("data/cave.comments.json", "w", encoding="utf-8")) 31 | await error.report( 32 | ( 33 | f"「新回声洞评论({cave_id}#{cave_comments[cave_id]['count'] - 1})」\n" 34 | f"{comment_text}\n" 35 | f"{event.get_session_id()}" 36 | ), 37 | feedback=False, 38 | ) 39 | await finish( 40 | "cave.commented", 41 | [cave_comments[cave_id]["count"] - 1], 42 | event.user_id, 43 | True, 44 | True, 45 | ) 46 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/create_manpage.py: -------------------------------------------------------------------------------- 1 | # [DEVELOP] 2 | from ._utils import * 3 | import os 4 | from .chatgptv2 import get_chatgpt_reply 5 | 6 | 7 | async def get_command_help(command_name: str, user_id: int) -> dict | None: 8 | if (data := Json("help.json")[command_name]) is not None: 9 | return data 10 | await finish("create_manpage.404", [], user_id, False, True) 11 | 12 | 13 | def generate_prompt(command_help: dict) -> str: 14 | prompt = """请参考以下指令帮助,写一个指令文档,需要包含名称、描述、权限、用法、示例(不包含输出示例)等信息,示例如下: 15 | 16 | ```markdown 17 | # 指令帮助 —— <命令名> 18 | 19 | ## 描述: 20 | ... 21 | 22 | ### 权限: 23 | 24 | - `everyone` 25 | 26 | ## 用法 27 | 28 | ### `<用法1(如 calc <表达式>)>` 29 | 30 | - 说明:<用法说明> 31 | - 示例:`<用法示例>` 32 | 33 | ### `<用法二>` 34 | 35 | - 说明:<用法说明> 36 | - 示例:`<用法示例>` 37 | 38 | 39 | 本 ManPage 使用 ChatGPT-3.5-Turbo 生成 40 | ``` 41 | 42 | 用法示例需要添加指令前缀`/` 43 | 44 | 替换`<...>`时需要将`<`和`>` 45 | 46 | 命令帮助如下: 47 | """ 48 | prompt += ( 49 | f"命令名:{command_help['name']}\n" 50 | f"简介([...]内为权限,以*开头(需要去掉*),使用空格分割,没有则为 *everyone):{command_help['info']}\n" 51 | "所有用法(<...>为必要参数,[...]为可选参数,{...|...}为选择参数):" 52 | ) 53 | for usage in command_help["usage"]: 54 | prompt += f"- {usage}" 55 | return prompt 56 | 57 | 58 | def push_changes(command_name) -> str: 59 | os.system("git add -A") 60 | os.system(f'git commit -a -m "[add] Create manpage for {command_name}"') 61 | return os.popen("git push").read() 62 | 63 | 64 | @create_command("create-manpage") 65 | async def create_manpage( 66 | _bot: Bot, event: MessageEvent, message: Message, matcher: Matcher = Matcher() 67 | ): 68 | session = await get_chatgpt_reply( 69 | [ 70 | { 71 | "role": "user", 72 | "content": generate_prompt( 73 | await get_command_help(message.extract_plain_text(), event.user_id) 74 | ), 75 | } 76 | ] 77 | ) 78 | await matcher.send(session["choices"][0]["message"]["content"]) 79 | try: 80 | os.mkdir(f"docs/{message.extract_plain_text()}") 81 | except OSError: 82 | pass 83 | with open( 84 | path := f"docs/{message.extract_plain_text()}/0.md", "w", encoding="utf-8" 85 | ) as f: 86 | f.write(session["choices"][0]["message"]["content"]) 87 | await send_text("create_manpage.saved", [path], event.user_id) 88 | await matcher.send(push_changes(message.extract_plain_text())) 89 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/duel_items.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from .etm.json2items import json2items 3 | from .etm.duel.item import DuelItem 4 | 5 | # [HELPSTART] Version: 2 6 | # Command: duel-items 7 | # Usage: duel-items 8 | # Msg: 查看装备 9 | # Info: 查看装备 10 | # [HELPEND] 11 | 12 | 13 | def get_items(user_id: int) -> list[DuelItem]: 14 | item_list = [] 15 | for item_data in Json(f"duel2/users/{user_id}.json").get("items", {}).values(): 16 | if isinstance(item_data, dict): 17 | items = [item_data] 18 | else: 19 | items = item_data 20 | item_list += [ 21 | i for i in json2items(items, str(user_id)) if isinstance(i, DuelItem) 22 | ] 23 | return item_list 24 | 25 | 26 | def get_item_list(user_id: int) -> str: 27 | length = 0 28 | text = "" 29 | for item in get_items(user_id): 30 | length += 1 31 | text += lang.text( 32 | "duel_items.list_item", 33 | [ 34 | lang.text(f"duel_items.type_{item.DUEL_ITEM_TYPE}", [], user_id), 35 | length, 36 | item.data["display_name"], 37 | item.count, 38 | ], 39 | user_id, 40 | ) 41 | return text 42 | 43 | 44 | @create_command("duel-items", {"duel-item", "duel-i"}) 45 | async def _(bot: Bot, event: MessageEvent, message: Message) -> None: 46 | # argv = message.extract_plain_text().split(" ") 47 | await finish( 48 | "duel_items.list", [get_item_list(event.user_id)], event.user_id, False, True 49 | ) 50 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/achievement.json: -------------------------------------------------------------------------------- 1 | { 2 | "什么欧皇": { 3 | "name": "什么欧皇", 4 | "condition": "在二十面骰子中获得 10 个「大失败」", 5 | "need_progress": 10, 6 | "award": [ 7 | { 8 | "id": "vimcoin", 9 | "count": 20, 10 | "data": {} 11 | }, 12 | { 13 | "id": "dice", 14 | "count": 3, 15 | "data": {} 16 | } 17 | ] 18 | }, 19 | "特性!特性": { 20 | "name": "特性!特性", 21 | "condition": "在二十面骰子中掷出 -1", 22 | "need_progress": 1, 23 | "award": [] 24 | }, 25 | "+0!": { 26 | "name": "+0!", 27 | "condition": "在每日签到中获得0vi", 28 | "need_progress": 1, 29 | "award": [ 30 | { 31 | "id": "vimcoin", 32 | "count": 15, 33 | "data": {} 34 | } 35 | ] 36 | }, 37 | "我爱数学": { 38 | "name": "我爱数学", 39 | "condition": "在速算中正确完成15次", 40 | "need_progress": 15, 41 | "award": [ 42 | { 43 | "id": "vimcoin", 44 | "count": 18, 45 | "data": {} 46 | } 47 | ] 48 | }, 49 | "遇水变大变高": { 50 | "name": "遇水变大变高", 51 | "condition": "使用一个「压缩毛巾」", 52 | "need_progress": 1, 53 | "award": [ 54 | { 55 | "id": "vimcoin", 56 | "count": 12, 57 | "data": {} 58 | } 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/achievement.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from . import bag 3 | from .. import _lang as lang 4 | from ..send_email import send_email 5 | from . import exp 6 | import json 7 | import time 8 | from . import data 9 | import os.path 10 | 11 | ACHIEVEMENTS = json.load( 12 | open( 13 | os.path.join(os.path.dirname(os.path.abspath(__file__)), "achievement.json"), 14 | encoding="utf-8", 15 | ) 16 | ) 17 | 18 | 19 | def get_user_achievement(user_id): 20 | try: 21 | return data.achi_user_data[user_id] 22 | except KeyError: 23 | return [] 24 | 25 | 26 | def change_user_achievement(user_id, _data): 27 | data.achi_user_data[user_id] = _data 28 | 29 | 30 | def unlock(name, user_id): 31 | user_achievement = get_user_achievement(user_id) 32 | if name in ACHIEVEMENTS.keys() and name not in user_achievement: 33 | user_achievement.append(name) 34 | asyncio.create_task( 35 | send_email( 36 | str(user_id), 37 | lang.text("achievement.email_title", [name], user_id), 38 | ACHIEVEMENTS[name]["condition"], 39 | ACHIEVEMENTS[name]["award"], 40 | ) 41 | ) 42 | 43 | 44 | def get_unlck_progress(name, user_id): 45 | user_data = data.achi_unlock_progress 46 | try: 47 | return user_data[user_id][name] 48 | except KeyError: 49 | return None 50 | 51 | 52 | def increase_unlock_progress(name, user_id, count=1): 53 | try: 54 | data.achi_unlock_progress[user_id][name] += count 55 | except KeyError: 56 | try: 57 | data.achi_unlock_progress[user_id][name] = count 58 | except KeyError: 59 | data.achi_unlock_progress[user_id] = {name: count} 60 | if data.achi_unlock_progress[user_id][name] >= ACHIEVEMENTS[name]["need_progress"]: 61 | unlock(name, user_id) 62 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/bag/crr.py: -------------------------------------------------------------------------------- 1 | from ..._utils import Json 2 | 3 | 4 | def add_crr(item_id: str, item_count: int, item_data: dict) -> None: 5 | data = Json("etm.crr.json") 6 | _id = 0 7 | while True: 8 | if _id not in data.keys(): 9 | data[str(_id)] = {"id": item_id, "count": item_count, "data": item_data} 10 | break 11 | _id += 1 12 | 13 | 14 | def pop_item(_id: str) -> dict: 15 | data = Json("etm.crr.json") 16 | return data.pop(_id) 17 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/bag/overflow.py: -------------------------------------------------------------------------------- 1 | from ..._utils import Json 2 | import time 3 | from .crr import add_crr 4 | 5 | 6 | def add_overflow(user_id: str, item_id: str, item_count: int, item_data: dict) -> None: 7 | data = Json("etm.overflow.json") 8 | data.update({user_id: {}}) 9 | _id = 0 10 | while True: 11 | if _id not in data[user_id]: 12 | data[user_id][str(_id)] = { 13 | "id": item_id, 14 | "count": item_count, 15 | "data": item_data, 16 | "time": time.time(), 17 | } 18 | break 19 | _id += 1 20 | data.changed_key.add(user_id) 21 | 22 | 23 | def get_overflow(user_id: str): 24 | check_user_overflow(user_id) 25 | return Json("etm.overflow.json").get(user_id, {}) 26 | 27 | 28 | def pop_item(user_id: str, _id: str) -> dict: 29 | data = Json("etm.overflow.json") 30 | data.changed_key.add(user_id) 31 | return data[user_id].pop(_id) 32 | 33 | 34 | def check_overflow_item(user_id: str, _id: str) -> None: 35 | if time.time() - Json("etm.overflow.json")[user_id][_id]["time"] >= 1800: 36 | item = pop_item(user_id, _id) 37 | add_crr(item["id"], item["count"], item["data"]) 38 | 39 | 40 | def check_user_overflow(user_id: str) -> None: 41 | data = Json("etm.overflow.json") 42 | data.update({user_id: {}}) 43 | for _id in data[user_id].keys(): 44 | check_overflow_item(user_id, _id) 45 | 46 | 47 | def check_overflow() -> None: 48 | for user_id in Json("etm.overflow.json").keys(): 49 | check_user_overflow(user_id) 50 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/buffs.json: -------------------------------------------------------------------------------- 1 | { 2 | "护符": { 3 | "is_gain": true, 4 | "max_effect": 5, 5 | "duration": null, 6 | "duel_buff": false 7 | }, 8 | "每日GPT限免": { 9 | "is_gain": true, 10 | "max_effect": 5, 11 | "duration": null, 12 | "duel_buff": false 13 | }, 14 | "GptPlus++": { 15 | "is_gain": true, 16 | "max_effect": 1, 17 | "duration": 2592000, 18 | "duel_buff": false 19 | } 20 | } -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/buff/CriticalStrikeChanceIncreased.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from . import Buff 3 | 4 | if TYPE_CHECKING: 5 | from plugins.Core.plugins.etm.duel.entity import Entity 6 | from types import NoneType 7 | 8 | 9 | class CriticalStrikeChanceIncreased(Buff): 10 | def __init__(self, entity: "Entity", value: float) -> None: 11 | super().__init__(entity) 12 | self.buff_id = "critical_strike_chance_increased" 13 | self.value = value 14 | self.name = self.text("name") 15 | self.description = self.text("description", [self.value * 100]) 16 | self.entity.logger.log("critical_strike_chance_increased", [self.value * 100]) 17 | self.entity.critical_strike = ( 18 | self.entity.critical_strike[0] + self.value, 19 | self.entity.critical_strike[1], 20 | ) 21 | 22 | def after_action(self) -> NoneType: 23 | self.entity.critical_strike = ( 24 | self.entity.critical_strike[0] - self.value, 25 | self.entity.critical_strike[1], 26 | ) 27 | return super().after_action() 28 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/buff/CriticalStrikeHarmIncreased.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from . import Buff 3 | 4 | if TYPE_CHECKING: 5 | from plugins.Core.plugins.etm.duel.entity import Entity 6 | from types import NoneType 7 | 8 | 9 | class CriticalStrikeHarmIncreased(Buff): 10 | def __init__(self, entity: "Entity", value: float) -> None: 11 | super().__init__(entity) 12 | self.buff_id = "critical_strike_harm_increased" 13 | self.value = value 14 | self.name = self.text("name") 15 | self.description = self.text("description", [self.value * 100]) 16 | self.entity.logger.log("critical_strike_harm_increased", [self.value * 100]) 17 | self.entity.critical_strike = ( 18 | self.entity.critical_strike[0], 19 | self.entity.critical_strike[1] + self.value, 20 | ) 21 | 22 | def after_action(self) -> NoneType: 23 | self.entity.critical_strike = ( 24 | self.entity.critical_strike[0], 25 | self.entity.critical_strike[1] - self.value, 26 | ) 27 | return super().after_action() 28 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/buff/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from ...._utils import * 3 | 4 | if TYPE_CHECKING: 5 | from ..entity import Entity 6 | 7 | 8 | class Buff: 9 | def __init__(self, entity: "Entity") -> None: 10 | self.buff_id: str = "" 11 | self.name: str = "" # TODO 12 | self.description: str = "无描述" # TODO 13 | self.adhesion: int = 0 14 | self.entity = entity 15 | if hasattr(self.entity, "user_id"): 16 | self.user_id = getattr(self.entity, "user_id") 17 | else: 18 | self.user_id = "default" 19 | 20 | def apply(self) -> None: 21 | self.entity.buff.append(self) 22 | 23 | def text(self, key: str, format_: list[Any] = []) -> str: 24 | return lang.text(f"{self.buff_id}.{key}", format_, self.user_id) 25 | 26 | def after_action(self) -> None: 27 | self.adhesion -= 1 28 | if self.adhesion <= 0: 29 | self.on_effect_end() 30 | self.entity.buff.remove(self) 31 | del self 32 | 33 | def on_effect_end(self) -> None: 34 | pass 35 | 36 | def on_start(self) -> None: 37 | pass 38 | 39 | def on_action(self, entities: "list[Entity]") -> None: 40 | pass 41 | 42 | def on_attack(self, harm: float) -> float: 43 | return harm 44 | 45 | def on_attacked(self, harm: float, entity: "Entity") -> float: 46 | return harm 47 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/buff/focus_down.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from . import Buff 3 | 4 | if TYPE_CHECKING: 5 | from plugins.Core.plugins.etm.duel.entity import Entity 6 | from types import NoneType 7 | 8 | 9 | class FocusDown(Buff): 10 | def __init__(self, entity: "Entity", value: float) -> None: 11 | super().__init__(entity) 12 | self.buff_id = "focus_down" 13 | self.value = value 14 | self.name = self.text("name") 15 | self.description = self.text("description", [self.value * 100]) 16 | self.entity.focus -= value 17 | 18 | def on_effect_end(self) -> NoneType: 19 | self.entity.focus += self.value 20 | super().on_effect_end() 21 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/buff/harm_up.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | from . import Buff 3 | 4 | if TYPE_CHECKING: 5 | from plugins.Core.plugins.etm.duel.entity import Entity 6 | from types import NoneType 7 | 8 | 9 | class HarmUpInt(Buff): 10 | def __init__(self, entity: "Entity", value: float) -> None: 11 | super().__init__(entity) 12 | self.buff_id = "harm_up_int" 13 | self.value = value 14 | self.name = self.text("name") 15 | self.description = self.text("description", [self.value * 100]) 16 | 17 | def on_attack(self, harm: float) -> float: 18 | harm += self.value 19 | return super().on_attack(harm) 20 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/item/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Literal 2 | from ...item import Item 3 | from ...exception import UserDataLocked 4 | from ...._utils import * 5 | 6 | if TYPE_CHECKING: 7 | from ..entity import Entity 8 | else: 9 | Entity = None 10 | 11 | 12 | class CannotUse(Exception): 13 | pass 14 | 15 | 16 | class Returned(Exception): 17 | pass 18 | 19 | 20 | class DuelItem(Item): 21 | DUEL_ITEM_TYPE: Literal["weapons", "consumables", "passive", "active", "other"] 22 | 23 | def init_duel(self, entity: Entity) -> None: 24 | self.entity: Entity = entity 25 | 26 | async def duel_use(self) -> None: 27 | raise CannotUse 28 | 29 | def use_item(self) -> None: 30 | if Json(f"duel2/lock.json")[self.user_id]: 31 | raise UserDataLocked 32 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/item/consumables.py: -------------------------------------------------------------------------------- 1 | from ..item import DuelItem 2 | 3 | 4 | class DuelConsumables(DuelItem): 5 | def __init__(self, count, data, user_id): 6 | super().__init__(count, data, user_id) 7 | self.DUEL_ITEM_TYPE = "consumables" 8 | 9 | def used(self) -> None: 10 | self.count -= 1 11 | if self.count <= 0: 12 | self.entity.items["other"].remove(self) 13 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/item/hand.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | # from plugins.Core.plugins.etm.duel.entity import Entity 4 | from .weapons import DuelWeapons 5 | 6 | if TYPE_CHECKING: 7 | from ..entity import Entity 8 | else: 9 | Entity = None 10 | 11 | 12 | class Hand(DuelWeapons): 13 | def on_register(self) -> None: 14 | self.item_id = "hand" 15 | self.basic_data = { 16 | "display_name": self.text("display_name"), 17 | "display_message": self.text("display_message"), 18 | } 19 | 20 | def init_duel(self, entity: Entity) -> None: 21 | super().init_duel(entity) 22 | entity.critical_strike = 0.25, 1.3 23 | 24 | def on_attack(self, entity: Entity, entities: list[Entity]) -> None: 25 | self.entity.logger.add_attack_log( 26 | entity, entity.attacked(self.get_harm(2), self.entity) 27 | ) 28 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/item/passive.py: -------------------------------------------------------------------------------- 1 | from ..item import DuelItem 2 | from typing import TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from ..entity import Entity 6 | 7 | 8 | class DuelPassiveItem(DuelItem): 9 | def __init__(self, count, data, user_id): 10 | super().__init__(count, data, user_id) 11 | self.DUEL_ITEM_TYPE = "passive" 12 | 13 | def on_attack(self, harm: float) -> float: 14 | return harm 15 | 16 | def on_attacked(self, harm: float, entity: "Entity") -> float: 17 | return harm 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/item/weapons.py: -------------------------------------------------------------------------------- 1 | import random 2 | from ...._utils import * 3 | 4 | # from ...bag import add_item 5 | from typing import TYPE_CHECKING, Literal 6 | from ..item import DuelItem 7 | 8 | if TYPE_CHECKING: 9 | from ..entity import Entity 10 | else: 11 | Entity = None 12 | from typing import Callable 13 | 14 | add_item: Callable[[str, str, int, dict], None] 15 | 16 | 17 | class DuelWeapons(DuelItem): 18 | ATTACK_RANGE: Literal["single", "all"] 19 | SET_NAME: Optional[str] 20 | ATTACK_TYPE: Literal["physical", "magic"] 21 | 22 | def __init__(self, count, data, user_id): 23 | super().__init__(count, data, user_id) 24 | self.DUEL_ITEM_TYPE = "weapons" 25 | self.ATTACK_RANGE = "single" 26 | self.ATTACK_TYPE = "physical" 27 | 28 | def on_attack(self, entity: Entity, entities: list[Entity]) -> None: 29 | pass 30 | 31 | def get_harm(self, basic_harm: float) -> float: 32 | harm = basic_harm 33 | for item in self.entity.items["passive"]: 34 | harm = item.on_attack(harm) or harm 35 | for buff in self.entity.buff: 36 | harm = buff.on_attack(harm) or harm 37 | if random.random() <= self.entity.critical_strike[0]: 38 | harm *= self.entity.critical_strike[1] 39 | return harm 40 | 41 | def use_item(self): 42 | super().use_item() 43 | data = Json(f"duel2/users/{self.user_id}.json") 44 | if not data["items"]: 45 | data["items"] = {} 46 | original_weapons: dict | None = data["items"].get("weapons") 47 | data["items"]["weapons"] = {"id": self.item_id, "count": 1, "data": self.data} 48 | data.changed_key.add("items") 49 | if original_weapons: 50 | add_item(self.user_id, original_weapons["id"], 1, original_weapons["data"]) 51 | self.count -= 1 52 | return lang.text("weapons.used", [self.data["display_name"]], self.user_id) 53 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/duel/scheduler.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from .entity import Entity 3 | from .logger import Logger 4 | 5 | 6 | class Scheduler: 7 | def __init__(self, entities: list[Entity], user_id: str = "default") -> None: 8 | self.original_entities: list[Entity] = entities 9 | self.entities = self.original_entities.copy() 10 | self.logger = Logger(user_id, self) 11 | for i in range(len(self.entities)): 12 | self.entities[i].set_logger(self.logger) 13 | self.logger.create_block() 14 | 15 | async def start(self) -> None: 16 | while (entity := self.get_entity()) and not self.is_finished(): 17 | self.logger.add_action_title(entity) 18 | self.logger.add_entity_hp(entity) 19 | reduce_value = entity.get_action_value() 20 | for e in self.entities: 21 | e.reduce_action_value(reduce_value) 22 | await entity.action(self.entities) 23 | for buff in entity.buff: 24 | buff.after_action() 25 | self.logger.create_block() 26 | self.logger.create_block() 27 | self.logger.log("finished") 28 | self.logger.create_block() 29 | 30 | def sort_entities(self) -> None: 31 | self.entities = sorted( 32 | self.original_entities, key=lambda e: e.get_action_value() 33 | ) 34 | 35 | def get_entity(self) -> Optional[Entity]: 36 | self.sort_entities() 37 | for e in self.entities: 38 | if e.hp != 0: 39 | return e 40 | 41 | def is_finished(self) -> bool: 42 | teams = {} 43 | for e in self.entities: 44 | if e.hp <= 0: 45 | continue 46 | if e.team_id not in teams: 47 | teams[e.team_id] = 0 48 | teams[e.team_id] += 1 49 | return len(teams.keys()) < 2 50 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/economy.py: -------------------------------------------------------------------------------- 1 | from .exception import IllegalQuantityException 2 | from . import user 3 | 4 | # from nonebot_plugin_apscheduler import scheduler 5 | from nonebot import require 6 | import json 7 | 8 | # from nonebot.log import logger 9 | from .data import vimcoin 10 | 11 | 12 | # @scheduler.scheduled_job("cron", hour="*/1", id="additem") 13 | async def add_item(): 14 | vimcoin["item_count"] += 175 15 | 16 | 17 | vimcoin["exchange_rate"] = 1 18 | 19 | # @scheduler.scheduled_job("cron", second="*/15", id="chamgeExchangeRate") 20 | """ 21 | async def change_exchange_rate(): 22 | # 总vi 23 | users = json.load(open("data/etm/users.json", encoding="utf-8")) 24 | all_vim = 0 25 | for user in list(users.values()): 26 | all_vim += user["vimcoin"] 27 | all_vi = all_vim * vimcoin["exchange_rate"] 28 | user_count = len(list(users.keys())) 29 | vimcoin["_exchange_rate"] += ((vimcoin["item_count"] / 30 | (all_vi / (user_count))) - 1) / 1000 31 | vimcoin["exchange_rate"] = vimcoin["_exchange_rate"] + 0.25 32 | json.dump(vimcoin, open("data/etm/vim.json", "w", encoding="utf-8")) 33 | """ 34 | 35 | 36 | def _add_vimcoin(user_id, count): 37 | data = user.get_user_data(user_id) 38 | data["vimcoin"] += count 39 | user.change_user_data(user_id, data) 40 | vimcoin["in"] += count 41 | 42 | 43 | def add_vimcoin(user_id, count): 44 | if count >= 0: 45 | _add_vimcoin(user_id, count) 46 | else: 47 | raise IllegalQuantityException(count) 48 | 49 | 50 | def _use_vimcoin(user_id, count): 51 | data = user.get_user_data(user_id) 52 | if data["vimcoin"] >= count: 53 | data["vimcoin"] -= count 54 | vimcoin["out"] += count 55 | user.change_user_data(user_id, data) 56 | return True 57 | else: 58 | return False 59 | 60 | 61 | def use_vimcoin(user_id, count): 62 | if count >= 0: 63 | return _use_vimcoin(user_id, count) 64 | else: 65 | raise IllegalQuantityException(count) 66 | 67 | 68 | def vi2vim(vi): 69 | return round(vi / vimcoin["exchange_rate"], 3) 70 | 71 | 72 | def _add_vi(user_id, count): 73 | _add_vimcoin(user_id, vi2vim(count)) 74 | 75 | 76 | def add_vi(user_id, count): 77 | add_vimcoin(user_id, vi2vim(count)) 78 | 79 | 80 | def remove_vi(user_id, count): 81 | _use_vimcoin(user_id, count) 82 | 83 | 84 | def use_vi(user_id, vi): 85 | used = vi2vim(vi) 86 | return use_vimcoin(user_id, used), used 87 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/exception.py: -------------------------------------------------------------------------------- 1 | class UserDataLocked(Exception): 2 | pass 3 | 4 | 5 | class IllegalQuantityException(Exception): 6 | pass 7 | 8 | 9 | class NoPawCoinException(Exception): 10 | pass 11 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/exp.py: -------------------------------------------------------------------------------- 1 | from . import user 2 | 3 | 4 | def _get_exp(user_id): 5 | return user.get_user_data(user_id)["exp"] 6 | 7 | 8 | def get_level(exp): 9 | level = 1 10 | while True: 11 | if exp > level**2: 12 | level += 1 13 | else: 14 | break 15 | return level 16 | 17 | 18 | def get_exp(user_id): 19 | exp = _get_exp(user_id) 20 | return exp - (get_level(exp) - 1) ** 2 21 | 22 | 23 | def get_user_level(user_id): 24 | """获取用户等级 25 | 26 | Args: 27 | user_id (str): 用户ID 28 | 29 | Returns: 30 | int: 用户的等级 31 | """ 32 | return get_level(_get_exp(user_id)) 33 | 34 | 35 | def add_exp(user_id, count): 36 | data = user.get_user_data(user_id) 37 | data["exp"] += count 38 | user.change_user_data(user_id, data) 39 | 40 | 41 | def _set_exp(user_id, count): 42 | data = user.get_user_data(user_id) 43 | data["exp"] = count 44 | user.change_user_data(user_id, data) 45 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/health.py: -------------------------------------------------------------------------------- 1 | from .._utils import * 2 | from .duel.entity.user import User 3 | 4 | 5 | def get_max_hp(user_id: int) -> int: 6 | return User(str(user_id)).max_hp 7 | 8 | 9 | def get_data(user_id: int, key: str) -> Any: ... 10 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/item_basic_data.py: -------------------------------------------------------------------------------- 1 | BASIC_DATA = { 2 | "useable": True, 3 | "can_be_sold": True, 4 | "dispoable": True, 5 | "maximum_stack": 64, 6 | "price": 5, 7 | "display_name": "", 8 | "display_message": "", 9 | } 10 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/item_list.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | 3 | from .items.unknown_item import UnknownItem 4 | from . import json2items 5 | from .item import Item 6 | from .items.dice import Dice 7 | from .items.auto_sign_coupon import AutoSignCoupon, AutoSignCouponActived 8 | from .items.talisman import Talisman 9 | from .items.rubbish import CommonRubbish 10 | from .items.book_and_quill import BookAndQuill 11 | from .items.pawcoin import PawCoin 12 | from .items.pouch import Pouch 13 | from .items.archfiend_dice import ArchfiendDice 14 | from .items.mystery_box import MysteryBoxLevel1, MysteriousShard, MysteryBoxLv3 15 | from .items.vimcoin import VimCoin 16 | from .items.experience import Experience 17 | from .items.gpt_monthly_pass import GptMonthlyPass 18 | from .items.towel_zip import TowelZip, Towel 19 | from .items.stick import Stick 20 | from .items.duel.passive.magic_cat import ( 21 | MagicCatHead, 22 | MagicCatBall, 23 | MagicCatFoot, 24 | MagicCatBody, 25 | MagicCatRope, 26 | MagicStick, 27 | ) 28 | from .items.duel.consumables.firecracker import Firecracker 29 | 30 | ITEMS = {} 31 | 32 | 33 | def register_item(item: Type[Item]) -> None: 34 | try: 35 | i = item(1, {}, None) 36 | except: 37 | pass 38 | ITEMS[i.item_id] = item 39 | json2items.item_helper.items[i.item_id] = item 40 | 41 | 42 | def register_items(items: list[Type[Item]]) -> None: 43 | for item in items: 44 | register_item(item) 45 | 46 | 47 | register_items( 48 | [ 49 | Dice, 50 | MysteryBoxLevel1, 51 | BookAndQuill, 52 | Talisman, 53 | Pouch, 54 | CommonRubbish, 55 | ArchfiendDice, 56 | TowelZip, 57 | MagicCatHead, 58 | Towel, 59 | VimCoin, 60 | PawCoin, 61 | GptMonthlyPass, 62 | Experience, 63 | MysteriousShard, 64 | AutoSignCoupon, 65 | AutoSignCouponActived, 66 | MysteryBoxLv3, 67 | Stick, 68 | MagicCatBall, 69 | MagicCatFoot, 70 | MagicCatBody, 71 | MagicCatRope, 72 | MagicStick, 73 | Firecracker, 74 | UnknownItem, 75 | ] 76 | ) 77 | 78 | # json2items.setup_items(ITEMS) 79 | 80 | # print("Loaded", ITEMS, json2items.ITEMS) 81 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/archfiend_dice.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from .. import economy 3 | from ..user import remove_hp 4 | import random 5 | from ..._utils import lang 6 | 7 | 8 | class ArchfiendDice(Item): 9 | def on_register(self): 10 | self.basic_data = { 11 | "display_name": "恶魔骰子", 12 | "display_message": ( 13 | "Straight from Kickback City!\n \n" 14 | "消耗 6.6vi 在 1 和 6 之间滚动,增益介于 -45HP 和 +45HP 之间。\n" 15 | "如果你掷出了 6,你将失去这个骰子并获得 75 vimcoins" 16 | ), 17 | "maximum_stack": 1, 18 | "price": 37, 19 | } 20 | self.item_id = "archfiend_dice" 21 | 22 | def use(self, _arg): 23 | if not economy.use_vimcoin(self.user_id, 6.6): 24 | return [lang.text("currency.no_money", [6.6], self.user_id)] 25 | match random.randint(1, 12): 26 | case 1 | 2: 27 | remove_hp(self.user_id, 45) 28 | return [lang.text("archfient_dice.info_1_5", [1, -45], self.user_id)] 29 | case 3 | 4: 30 | remove_hp(self.user_id, 30) 31 | return [lang.text("archfient_dice.info_1_5", [2, -30], self.user_id)] 32 | case 5 | 6 | 7: 33 | remove_hp(self.user_id, 15) 34 | return [lang.text("archfient_dice.info_1_5", [3, -15], self.user_id)] 35 | case 8 | 9: 36 | remove_hp(self.user_id, -15) 37 | return [lang.text("archfient_dice.info_1_5", [4, "+15"], self.user_id)] 38 | case 10 | 11: 39 | remove_hp(self.user_id, -30) 40 | return [lang.text("archfient_dice.info_1_5", [5, "+30"], self.user_id)] 41 | case 12: 42 | remove_hp(self.user_id, -45) 43 | economy.add_vi(self.user_id, 75) 44 | self.count = 0 45 | return [lang.text("archfient_dice.info_6", [], self.user_id)] 46 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/auto_sign_coupon.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from .. import bag 3 | from ... import _lang 4 | from .. import data as _data 5 | from ... import _sign 6 | from ... import send_email 7 | import time 8 | from nonebot import require 9 | from nonebot_plugin_apscheduler import scheduler 10 | 11 | require("nonebot_plugin_apscheduler") 12 | 13 | 14 | class AutoSignCoupon(Item): 15 | def on_register(self) -> None: 16 | self.item_id = "auto_sign_coupon" 17 | self.basic_data = { 18 | "display_name": "自动签到券(未激活)", 19 | "display_message": "使用一次激活后可自动签到", 20 | "maximum_stack": 64, 21 | } 22 | 23 | def use_item(self): 24 | bag.add_item(self.user_id, "auto_sign_coupon_actived", 1, {}) 25 | return _lang.text("asc.actived", [], self.user_id) 26 | 27 | 28 | class AutoSignCouponActived(Item): 29 | def on_register(self): 30 | self.item_id = "auto_sign_coupon_actived" 31 | self.basic_data = { 32 | "display_name": "自动签到券(激活)", 33 | "display_message": "可自动签到,再次使用取消激活", 34 | "maximum_stack": 64, 35 | } 36 | 37 | def used(self): 38 | self.count -= 1 39 | 40 | def use_item(self): 41 | bag.add_item(self.user_id, "auto_sign_coupon", 1, {}) 42 | return _lang.text("asc.inactived", [], self.user_id) 43 | 44 | 45 | @scheduler.scheduled_job( 46 | "cron", second="0", minute="55", hour="23", day="*", id="auto_sign_coupon" 47 | ) 48 | async def auto_sign_coupon(): 49 | users = _data.basic_data.keys() 50 | for u in users: 51 | ub = bag.get_user_bag(u) 52 | for l in range(len(ub)): 53 | i = ub[l] 54 | if i.item_id == "auto_sign_coupon_actived": 55 | info = _sign._sign(u) 56 | if info != "主人今天已经签到过了喵!": 57 | ub[l].used() 58 | _info = _lang.text("asc.signed_2", [], u) + "\n" + info 59 | else: 60 | _info = _lang.text("asc.signed_3", [], u) 61 | await send_email.send_email( 62 | u, _lang.text("asc.signed_1", [time.strftime("%Y-%m-%d")], u), _info 63 | ) 64 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/book_and_quill.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from ..._lang import text 3 | import nonebot 4 | from .. import exp 5 | 6 | 7 | class BookAndQuill(Item): 8 | def on_register(self): 9 | self.item_id = "book_and_quill" 10 | self.basic_data = { 11 | "display_name": "书与笔", 12 | "display_message": "", 13 | "price": 8, 14 | "maximum_stack": 1, 15 | "data": "", 16 | "author": None, 17 | "saved": False, 18 | } 19 | 20 | async def async_use(self, _argv=""): 21 | argv = _argv.split(" ") 22 | if argv[0] == "--write": 23 | if not self.data["saved"]: 24 | self.data["data"] = " ".join(argv[1:]) 25 | return [text("etm.book_written", [], self.user_id)] 26 | else: 27 | return [text("etm.book_saved", [], self.user_id)] 28 | elif argv[0] == "--save": 29 | if not self.data["saved"]: 30 | self.data["author"] = self.user_id 31 | self.data["saved"] = True 32 | self.data["display_name"] = " ".join( 33 | _argv.splitlines()[0].split(" ")[1:] 34 | ) 35 | self.data["display_message"] = "\n".join(_argv.splitlines()[1:]) 36 | exp.add_exp(self.user_id, 4) 37 | return [text("etm.book_savesuccess", [], self.user_id)] 38 | else: 39 | return [text("etm.book_saved", [], self.user_id)] 40 | else: 41 | author_nickname = ( 42 | await list(nonebot.get_bots().values())[0].get_stranger_info( 43 | user_id=self.data["author"] 44 | ) 45 | )["nickname"] 46 | author = f"{author_nickname} ({self.data['author']})" 47 | return [ 48 | text( 49 | "etm.book_read", 50 | [self.data["display_name"], author, self.data["data"]], 51 | ) 52 | ] 53 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/duel/consumables/firecracker.py: -------------------------------------------------------------------------------- 1 | import random 2 | from types import NoneType 3 | from ....duel.item import CannotUse, Returned 4 | from ....duel.item.consumables import DuelConsumables 5 | from ....duel.entity.user import User 6 | from ....._utils import * 7 | from ....duel.buff.CriticalStrikeHarmIncreased import CriticalStrikeHarmIncreased 8 | 9 | 10 | class Firecracker(DuelConsumables): 11 | 12 | def __init__(self, count, data, user_id): 13 | super().__init__(count, data, user_id) 14 | self.DUEL_ITEM_TYPE = "consumables" 15 | 16 | def on_register(self) -> None: 17 | self.item_id = "firecracker" 18 | self.setup_basic_data( 19 | display_name=self.text("display_name"), 20 | display_message=self.text("display_message"), 21 | maximum_stack=16, 22 | ) 23 | 24 | async def duel_use(self) -> NoneType: 25 | await super().duel_use() 26 | if not isinstance(self.entity, User): 27 | self.entity.logger.log("unsupported_not_user") 28 | raise CannotUse 29 | entities = [ 30 | entity 31 | for entity in self.entity.logger.scheduler.entities 32 | if entity.hp > 0 and entity.team_id != self.entity.team_id 33 | ] 34 | if not entities: 35 | self.entity.logger.log("no_entity", []) 36 | raise CannotUse 37 | text = "" 38 | length = 0 39 | for entity in entities: 40 | length += 1 41 | text += lang.text( 42 | "duel_user.attack_menu_item", 43 | [length, entity.name, entity.team_id], 44 | self.user_id, 45 | ) 46 | await self.entity.send("duel_user.attack_menu", [text, length + 1]) 47 | choice = await self.entity.receive_reply( 48 | choices=[str(i) for i in range(1, length + 2)], default=str(length + 1) 49 | ) 50 | if choice is None or choice == str(length + 1): 51 | raise Returned 52 | self.entity.logger.add_attack_log( 53 | entities[int(choice) - 1].attacked(20, self.entity, attack_type="physical") 54 | ) 55 | self.entity.logger.add_attack_log( 56 | self.entity.attacked(3, self.entity, attack_type="physical") 57 | ) 58 | if random.random() <= 0.03: 59 | buff = CriticalStrikeHarmIncreased(self.entity, 3) 60 | buff.adhesion = 7 61 | buff.apply() 62 | self.used() 63 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/experience.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from .. import exp 3 | 4 | 5 | class Experience(Item): 6 | def on_register(self): 7 | self.item_id = "exp" 8 | self.basic_data = { 9 | "display_name": "Experience", 10 | "display_message": "经验", 11 | "maximum_stack": 1145141919810, 12 | } 13 | 14 | def _after_register(self): 15 | if self.user_id: 16 | exp.add_exp(self.user_id, self.count) 17 | self.count = 0 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/gpt_monthly_pass.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from ..buff import add_buff 3 | import time 4 | 5 | 6 | class GptMonthlyPass(Item): 7 | 8 | def on_register(self): 9 | self.item_id = "gpt_monthly_pass" 10 | self.basic_data = { 11 | "display_name": self.text("display_name"), 12 | "display_message": self.text("display_message"), 13 | "maximum_stack": 8, 14 | "price": 1200, 15 | } 16 | 17 | def use(self, args): 18 | add_buff(self.user_id, "GptPlus++") 19 | return [ 20 | self.text( 21 | "used", 22 | [time.strftime("%Y-%m-%d", time.localtime(time.time() + 2592000))], 23 | ) 24 | ] 25 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/pawcoin.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | 3 | 4 | class PawCoin(Item): 5 | def on_register(self): 6 | self.basic_data = { # type: ignore 7 | "display_name": "猫爪币", 8 | "display_message": "中间印着一个猫爪的硬币,可用于「万能合成机」合成使用,也可用于进行游戏\n \n「喵,喵。喵?」", 9 | "useable": False, 10 | "price": 3, 11 | } 12 | self.item_id = "pawcoin" 13 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/rubbish.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | from ..merger import merge_item_list 3 | from ..item import Item 4 | import random 5 | from ..json2items import json2items 6 | 7 | 8 | def add_item(user_id, item_id, item_count=1, item_data={}): ... 9 | 10 | 11 | class CommonRubbish(Item): 12 | ITEM_ID_LIST = ["pouch", "towel.zip", "towel", "mysterious_shard", "book_and_quill"] 13 | 14 | def on_register(self): 15 | self.item_id = "common_rubbish" 16 | self.setup_basic_data( 17 | display_name=self.text("display_name"), 18 | display_message=self.text("display_message"), 19 | price=10, 20 | ) 21 | 22 | def get_items(self) -> list[dict[str, Any]]: 23 | return self.data.get( 24 | "items", 25 | [ 26 | {"id": item_id, "count": random.randint(1, 5), "data": {}} 27 | for item_id in random.choices(self.ITEM_ID_LIST, k=2) 28 | ], 29 | ) 30 | 31 | def use(self, args): 32 | try: 33 | count = int(args) 34 | except BaseException: 35 | count = 1 36 | count = min(count, self.count) 37 | 38 | items = [item_list for item_list in self.get_items() for _ in range(count)] 39 | self.count -= count 40 | 41 | items = merge_item_list(json2items(items)) 42 | message = self.text("use_title", [count]) 43 | length = 1 44 | for item in items: 45 | add_item(self.user_id, item.item_id, item.count, item.data) 46 | message += self.text( 47 | "use_item", [length, item.data["display_name"], item.count] 48 | ) 49 | length += 1 50 | return [message] 51 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/stick.py: -------------------------------------------------------------------------------- 1 | from ..duel.entity import Entity 2 | from ..duel.item.weapons import DuelWeapons 3 | 4 | 5 | class Stick(DuelWeapons): 6 | def on_register(self): 7 | self.item_id = "stick" 8 | self.basic_data = { 9 | "display_name": self.text("display_name"), 10 | "display_message": self.text("display_message", [self.user_id]), 11 | "price": 3, 12 | "maximum_stack": 16, 13 | } 14 | 15 | def init_duel(self, entity: Entity) -> None: 16 | super().init_duel(entity) 17 | self.entity.critical_strike = (0.25, 1.35) 18 | 19 | def on_attack(self, entity: Entity, entities: list[Entity]) -> None: 20 | self.entity.logger.add_attack_log( 21 | entity.attacked(self.get_harm(5), self.entity) 22 | ) 23 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/talisman.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from .. import buff 3 | 4 | 5 | class Talisman(Item): 6 | def on_register(self): 7 | self.item_id = "talisman" 8 | self.basic_data = { 9 | "display_name": "护符", 10 | "display_message": "在二十面骰子出 1 时有 75% 的概率取消扣除 50vi,生效 5 次", 11 | "maximum_stack": 8, 12 | "price": 87, 13 | } 14 | 15 | def use_item(self): 16 | buff.add_buff(self.user_id, "护符", 1) 17 | return "护符已生效" 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/towel_zip.py: -------------------------------------------------------------------------------- 1 | from .. import bag 2 | from ..item import Item 3 | from .. import achievement 4 | from ... import _lang 5 | 6 | 7 | class TowelZip(Item): 8 | def on_register(self): 9 | self.item_id = "towel.zip" 10 | self.basic_data = { 11 | "display_name": "压缩毛巾", 12 | "display_message": "我们这个压缩毛巾体积小方便携带,拆开一包,放水里就变大,怎么扯都扯不坏,用来擦脚,擦脸,擦嘴都是很好用的,你看打开以后像圆饼一样大小,放在水里遇水变大变高,吸水性很强的。打开以后,是一条加大加厚的毛巾,你看他怎么挣都挣不坏,好不掉毛不掉絮,使用七八次都没问题,出差旅行带上它非常方便,用它擦擦脚,再擦擦嘴,擦擦脸,干净卫生。什么?在哪里买?下方小黄车,买五包送五包,还包邮。", 13 | "price": 9.9, 14 | } 15 | 16 | def use_item(self): 17 | achievement.unlock("遇水变大变高", self.user_id) 18 | bag.add_item(self.user_id, "towel", 16, {}) 19 | return _lang.text("towel_zip.used", [], self.user_id) 20 | 21 | 22 | class Towel(Item): 23 | def on_register(self): 24 | self.item_id = "towel" 25 | self.basic_data = { 26 | "display_name": "毛巾", 27 | "display_message": "我们这个压缩毛巾体积小方便携带,拆开一包,放水里就变大,怎么扯都扯不坏,用来擦脚,擦脸,擦嘴都是很好用的,你看打开以后像圆饼一样大小,放在水里遇水变大变高,吸水性很强的。打开以后,是一条加大加厚的毛巾,你看他怎么挣都挣不坏,好不掉毛不掉絮,使用七八次都没问题,出差旅行带上它非常方便,用它擦擦脚,再擦擦嘴,擦擦脸,干净卫生。什么?在哪里买?下方小黄车,买五包送五包,还包邮。", 28 | "price": 0.62, 29 | "useable": False, 30 | } 31 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/unknown_item.py: -------------------------------------------------------------------------------- 1 | from types import NoneType 2 | from ..item import Item 3 | 4 | 5 | class UnknownItem(Item): 6 | 7 | def on_register(self) -> None: 8 | self.item_id = "unknown" 9 | self.setup_basic_data( 10 | display_name=self.text("display_name"), 11 | display_message=self.text("display_message"), 12 | ) 13 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/items/vimcoin.py: -------------------------------------------------------------------------------- 1 | from ..item import Item 2 | from .. import economy 3 | 4 | 5 | class VimCoin(Item): 6 | def on_register(self): 7 | self.item_id = "vimcoin" 8 | self.basic_data = { 9 | "display_name": "VimCoin", 10 | "display_message": "XDbot2 通用货币", 11 | "maximum_stack": 4096, 12 | } 13 | 14 | def _after_register(self): 15 | if self.user_id: 16 | economy.add_vi(self.user_id, self.count) 17 | self.count = 0 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/json2items.py: -------------------------------------------------------------------------------- 1 | # from .item_list import ITEMS 2 | # from .duel.entity import user 3 | from typing import Type 4 | 5 | from .items.unknown_item import UnknownItem 6 | from .item import Item 7 | 8 | # from . import get_items 9 | # from .items import ( 10 | # mystery_box, 11 | # rubbish, 12 | # pouch 13 | # ) 14 | 15 | 16 | class ItemsHelper: 17 | def __init__(self) -> None: 18 | self.items = {} 19 | # print("1"*30) 20 | 21 | 22 | item_helper = ItemsHelper() 23 | 24 | 25 | def json2items(items: list[dict], user_id=None) -> list[Item]: 26 | # print(item_helper.items) 27 | item_list = [] 28 | for item in items: 29 | try: 30 | item_list.append( 31 | (item_helper.items[item["id"]](item["count"], item["data"], user_id)) 32 | ) 33 | except KeyError: 34 | item_list.append(UnknownItem(item["count"], item["data"], user_id)) 35 | return item_list 36 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/merger.py: -------------------------------------------------------------------------------- 1 | from .item import Item 2 | 3 | 4 | def merge_item_list(original_item_list: list[Item]) -> list[Item]: 5 | item_list: list[Item] = [] 6 | for item in original_item_list: 7 | for item2 in item_list: 8 | if ( 9 | item2.item_id == item.item_id 10 | and item2.data == item.data 11 | and item2.count < item2.data["maximum_stack"] 12 | ): 13 | count = min(item2.data["maximum_stack"] - item2.count, item.count) 14 | item.count -= count 15 | item2.count += count 16 | if item.count == 0: 17 | break 18 | else: 19 | if item.count <= item.data["maximum_stack"]: 20 | item_list.append(item) 21 | else: 22 | count = item.count 23 | item.count = 1 24 | item_list += merge_item_list([item for _ in range(count)]) 25 | return item_list 26 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/nbt.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | 4 | class NbtDict(TypedDict): 5 | useable: bool 6 | can_be_sold: bool 7 | dispoable: bool 8 | maximum_stack: int 9 | price: int 10 | display_name: str 11 | display_message: str 12 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/registry/registries.py: -------------------------------------------------------------------------------- 1 | from . import registry 2 | from ..item import Item 3 | import typing 4 | 5 | ItemConstructor = typing.Callable[[int, dict[str, typing.Any], str], Item] 6 | 7 | ITEMS: registry.Registry[ItemConstructor] = registry.Registry() 8 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/registry/registry.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | T1 = typing.TypeVar("T1") 4 | 5 | 6 | class ResourceLocation: 7 | 8 | namespace: str 9 | path: str 10 | 11 | def __init__(self, namespace: str, path: str): 12 | self.namespace = namespace 13 | self.path = path 14 | 15 | def getNamespace(self) -> str: 16 | return self.namespace 17 | 18 | def getPath(self) -> str: 19 | return self.path 20 | 21 | def __hash__(self) -> int: 22 | return str(self).__hash__() 23 | 24 | def __str__(self) -> str: 25 | return f"{self.getNamespace()}:{self.getPath()}" 26 | 27 | 28 | class Registry(typing.Generic[T1]): 29 | 30 | _map: dict[ResourceLocation, T1] 31 | 32 | def __init__(self): 33 | self._map = dict() 34 | self._tagManager = TagManager(self) 35 | 36 | def getTags(self): 37 | return self._tagManager 38 | 39 | def registry( 40 | self, location: ResourceLocation, value: T1 41 | ) -> tuple[ResourceLocation, T1]: 42 | if location in self._map.keys() or value in self._map.values(): 43 | raise Exception("Duplicate entry") 44 | self._map[location] = value 45 | return (location, value) 46 | 47 | def getValue(self, location: ResourceLocation) -> T1: 48 | return self._map[location] 49 | 50 | def getKey(self, value: T1) -> ResourceLocation: 51 | for first, second in self._map.items(): 52 | if value == second: 53 | return first 54 | raise ValueError(value) 55 | 56 | def getEntries(self) -> list[tuple[ResourceLocation, T1]]: 57 | entries = list(self._map.items()) 58 | return entries 59 | 60 | 61 | class TagManager(typing.Generic[T1]): 62 | _map: dict[ResourceLocation, list[T1]] 63 | _registry: Registry[T1] 64 | 65 | def __init__(self, registry: Registry[T1]): 66 | self.registry = registry 67 | 68 | def getEntries(self): 69 | entries = list(self._map.items()) 70 | return entries 71 | 72 | def getValues(self, location: ResourceLocation) -> list[T1]: 73 | return self._map[location] 74 | 75 | def getKeys(self, value: T1) -> list[ResourceLocation]: 76 | keys = list() 77 | for first, second in self._map.items(): 78 | if value in second: 79 | keys += first 80 | return keys 81 | 82 | def put(self, name: ResourceLocation, *args: tuple[T1]): 83 | for it in args: 84 | self._map[name].append(it) 85 | return self 86 | 87 | def contains(self, name: ResourceLocation, value: T1): 88 | return value in self._map[name] 89 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/etm/user.py: -------------------------------------------------------------------------------- 1 | from . import data as _data 2 | from .._utils import * 3 | from .._messenger import send_message 4 | from .health import get_max_hp 5 | from .._utils import Json 6 | from .exception import UserDataLocked 7 | 8 | # from .duel.entity import user 9 | 10 | RAW_DATA = {"user_id": None, "exp": 0, "health": 100, "vimcoin": 0} 11 | 12 | 13 | def get_user_data(user_id): 14 | try: 15 | return _data.basic_data[str(user_id)].copy() 16 | except KeyError: 17 | data = RAW_DATA.copy() 18 | data["user_id"] = str(user_id) 19 | data["health"] = get_max_hp(user_id) 20 | return data 21 | 22 | 23 | def change_user_data(user_id, data): 24 | _data.basic_data[str(user_id)] = data 25 | 26 | 27 | def get_hp(user_id: int) -> int: 28 | if (user_data := get_user_data(user_id))["health"] >= ( 29 | max_hp := get_max_hp(user_id) 30 | ): 31 | user_data["health"] = max_hp 32 | change_user_data(user_id, user_data) 33 | return user_data["health"] 34 | 35 | 36 | def remove_hp(user_id: int, count: float) -> None: 37 | if Json(f"duel2/lock.json")[str(user_id)]: 38 | raise UserDataLocked 39 | user_data = get_user_data(user_id) 40 | user_data["health"] -= count 41 | if user_data["health"] <= 0: 42 | origin_vimcoin = user_data["vimcoin"] 43 | user_data["vimcoin"] *= 0.5 if user_data["vimcoin"] > 0 else 1.5 44 | send_message( 45 | lang.text( 46 | "etm_user.died", 47 | [reduced_vimcoin := origin_vimcoin - user_data["vimcoin"]], 48 | user_id, 49 | ), 50 | str(user_id), 51 | ) 52 | user_data["health"] = get_max_hp(user_id) 53 | Json("droped_vimcoin.json")["count"] = ( 54 | Json("droped_vimcoin.json").get("count", 0) + reduced_vimcoin 55 | ) 56 | change_user_data(user_id, user_data) 57 | 58 | 59 | # user.get_hp = get_hp 60 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/extra.py: -------------------------------------------------------------------------------- 1 | # [HELPSTART] Version: 2 2 | # Command: train 3 | # Msg: 查询铁路信息 4 | # Info: 通过 12306 查询铁路车次信息 5 | # Usage: train <车次> 6 | # Command: 服务状态 7 | # Msg: 查看网络状态 8 | # Info: 查看 XDbot2 有关服务状态 9 | # Usage: 服务状态 10 | # Command: motd 11 | # Msg: MC服务器信息 12 | # Info: 查询 Minecraft 服务器信息 13 | # Usage: motd:查看插件帮助 14 | # Usage: motdpe <server>:查询基岩版服务器信息 15 | # Usage: motd <server>:查询 Java 版服务器信息 16 | # [HELPEND] 17 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/forward.py: -------------------------------------------------------------------------------- 1 | import json 2 | import traceback 3 | from . import _error 4 | from . import _lang 5 | from nonebot import on_message 6 | from nonebot.adapters.onebot.v11 import ( 7 | Bot, 8 | GroupMessageEvent, 9 | Message, 10 | PrivateMessageEvent, 11 | ) 12 | 13 | ctrlGroup = json.load(open("data/ctrl.json", encoding="utf-8"))["control"] 14 | # privateForward = on_message() 15 | groupForward = on_message() 16 | forwardData = json.load(open("data/forward.groupList.json", encoding="utf-8")) 17 | 18 | 19 | # @privateForward.handle() 20 | # async def privateForwardHandle(bot: Bot, event: PrivateMessageEvent): 21 | # try: 22 | # if event.get_message().extract_plain_text()[1:11] != "gpt-config": 23 | # await _error.report( 24 | # f"""{_lang.text("forward.private")} 25 | # {_lang.text("forward._user")}{(await bot.get_stranger_info(user_id=event.get_user_id()))['nickname']} ({event.get_user_id()}) 26 | # {event.get_message()}""" 27 | # ) 28 | 29 | # except Exception: 30 | # await _error.report(traceback.format_exc(), privateForward) 31 | 32 | 33 | @groupForward.handle() 34 | async def groupForwardHandle(bot: Bot, event: GroupMessageEvent): 35 | try: 36 | if str(event.group_id) in forwardData: 37 | await _error.report( 38 | f"""{_lang.text("forward.group")} 39 | {_lang.text("forward._group")}{event.get_session_id().split('_')[1]} 40 | {_lang.text("forward._user")}{(await bot.get_stranger_info(user_id=event.user_id))['nickname']} ({event.get_user_id()}) 41 | {event.get_message()}""" 42 | ) 43 | 44 | except Exception: 45 | await _error.report(traceback.format_exc()) 46 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/gpt_config.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | 3 | # [HELPSTART] Version: 2 4 | # Command: gpt-config 5 | # Usage: gpt-config 16k:启用/禁用 16k 模型(建议 Plus 开启) 6 | # Usage: gpt-config auto-pay:启用/禁用在 TOKEN 不足时自动支付 7 | # Msg: GPT设置 8 | # Info: XDbot2 GPT 设置 9 | # [HELPEND] 10 | 11 | 12 | @create_command("gpt-config") 13 | async def _(bot: Bot, event: MessageEvent, message: Message) -> None: 14 | argv = message.extract_plain_text().split(" ") 15 | user_id = event.user_id 16 | match argv[0]: 17 | case "16k": 18 | await set_switch( 19 | f"gpt/users/{user_id}.json", "16k", get_list_item(argv, 1, ""), user_id 20 | ) 21 | case "auto-pay": 22 | await set_switch( 23 | f"gpt/users/{user_id}.json", 24 | "automatic_payment", 25 | get_list_item(argv, 1, ""), 26 | user_id, 27 | ) 28 | case _: 29 | await finish(get_currency_key("wrong_argv"), ["gpt-config"], event.user_id) 30 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/gpt_scale.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from .chatgptv2 import ( 3 | get_session_messages, 4 | get_user_session, 5 | add_message_to_session, 6 | reset_session, 7 | get_chatgpt_reply, 8 | ) 9 | from nonebot_plugin_apscheduler import scheduler 10 | 11 | 12 | async def scale_session(session_id: str) -> str | None: 13 | add_message_to_session(session_id, "system", "请总结这个会话包含的要点") 14 | memory = ( 15 | (await get_chatgpt_reply(get_session_messages(session_id))) 16 | .choices[0] 17 | .message.content 18 | ) 19 | reset_session(session_id, "default") 20 | add_message_to_session(session_id, "user", memory or "") 21 | return memory 22 | 23 | 24 | # [HELPSTART] Version: 2 25 | # Command: gpt-scale 26 | # Msg: 缩减GPT会话 27 | # Info: 缩减当前 XDbot2GPT 会话(可能丢失部分信息) 28 | # Usage: gpt-scale 29 | # [HELPEND] 30 | 31 | 32 | @create_command("gpt-scale") 33 | async def _( 34 | bot: Bot, event: MessageEvent, message: Message, matcher: Matcher = Matcher() 35 | ) -> None: 36 | await matcher.finish(await scale_session(get_user_session(event.get_user_id()))) 37 | 38 | 39 | @scheduler.scheduled_job("cron", hour="0", minute="0", second="0", id="scale_sessions") 40 | async def scale_sessions() -> None: 41 | for session_file in os.listdir("data/gpt/sessions"): 42 | session_id = session_file[:-5] 43 | if len(get_session_messages(session_id)) < 5: 44 | continue 45 | await scale_session(session_id) 46 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/latex.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from io import BytesIO 3 | import matplotlib.pyplot as plt 4 | 5 | # plt.rcParams['text.usetex'] = True 6 | 7 | 8 | def generate_image(latex_formula: str) -> MessageSegment: 9 | fig, ax = plt.subplots() 10 | ax.axis("off") 11 | ax.text( 12 | 0.5, 13 | 0.5, 14 | latex_formula, 15 | transform=ax.transAxes, 16 | fontsize=18, 17 | va="center", 18 | ha="center", 19 | ) 20 | buffer = BytesIO() 21 | plt.savefig(buffer, bbox_inches="tight", pad_inches=0.1) 22 | return MessageSegment.image(buffer) 23 | 24 | 25 | @create_command("latex") 26 | async def _(bot, event: MessageEvent, message: Message) -> None: 27 | await finish( 28 | "latex.latex", 29 | [generate_image(f"${message.extract_plain_text()}$")], 30 | event.user_id, 31 | parse_cq_code=True, 32 | ) 33 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/lgs.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | 3 | 4 | @create_command("launch-genshin-impact", {"lgs", "lgi"}) 5 | async def _(_bot, event: MessageEvent, message: Message): 6 | if message.extract_plain_text().strip() == "status": 7 | await finish( 8 | "lgs.status", 9 | [Json("data/lgs.count.json")[event.get_user_id()]], 10 | event.user_id, 11 | ) 12 | Json("data/lgs.count.json").add(event.get_user_id(), 1) 13 | await finish("lgs.launch", [], event.user_id, False, True) 14 | 15 | 16 | # [HELPSTART] Version: 2 17 | # Command: lgs 18 | # Usage: launch-genshin-impact:启动原神 19 | # Usage: lgs status:查看原神启动次数 20 | # Info: 打开原神 21 | # [HELPEND] 22 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/linuxkernelnews.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11.bot import Bot 2 | 3 | # from nonebot.adapters.onebot.v11.event import MessageEvent 4 | # from nonebot.adapters.onebot.v11 import Message 5 | # from nonebot.params import CommandArg 6 | from . import _error 7 | from . import _lang 8 | from nonebot.exception import FinishedException 9 | from nonebot import on_command 10 | from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent 11 | import json 12 | import traceback 13 | import re 14 | import httpx 15 | 16 | ctrlGroup = json.load(open("data/ctrl.json", encoding="utf-8"))["control"] 17 | linuxkernelnews = on_command("linuxkernelnews", aliases={"lkn"}) 18 | 19 | 20 | @linuxkernelnews.handle() 21 | async def linuxkernelnewsHandle(bot: Bot, event: MessageEvent): 22 | try: 23 | async with httpx.AsyncClient() as client: 24 | req = await client.get("https://www.kernel.org/feeds/kdist.xml") 25 | data = req.read().decode("utf-8") 26 | data = re.findall( 27 | r'https://cdn.kernel.org/pub/linux/kernel/.*/linux-.*.tar.xz"', data 28 | ) 29 | kernels = "" 30 | for i in range(len(data)): 31 | kernels += f"{i+1}. {data[i][:-1].replace('cdn.kernel.org/pub','mirrors.ustc.edu.cn/kernel.org')}\n" 32 | answer = f"""kernel.org {_lang.text('linuxkernelnews.new',[],event.get_user_id())} 33 | {kernels}""" 34 | await linuxkernelnews.finish(answer) 35 | except FinishedException: 36 | raise FinishedException() 37 | except Exception: 38 | await _error.report(traceback.format_exc(), linuxkernelnews) 39 | 40 | 41 | # [HELPSTART] Version: 2 42 | # Command: lkn 43 | # Usage: lkn 44 | # Msg: 查看最新linux内核 45 | # Info: linuxkernelnews(查看最新的linux内核) 46 | # [HELPEND] 47 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/linuxman.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11.bot import Bot 2 | from nonebot.adapters.onebot.v11.event import GroupMessageEvent 3 | from nonebot.adapters.onebot.v11 import Message, MessageSegment 4 | from nonebot.params import CommandArg 5 | from nonebot.exception import FinishedException 6 | from nonebot import on_command 7 | from nonebot.exception import ActionFailed 8 | import json 9 | from . import _error 10 | from . import _lang 11 | import traceback 12 | import httpx 13 | from typing import List 14 | 15 | ctrlGroup = json.load(open("data/ctrl.json", encoding="utf-8"))["control"] 16 | linuxman = on_command("linuxman") 17 | 18 | 19 | @linuxman.handle() 20 | async def linuxmanHandle( 21 | bot: Bot, event: GroupMessageEvent, message: Message = CommandArg() 22 | ): 23 | try: 24 | argument = message.extract_plain_text() 25 | async with httpx.AsyncClient() as client: 26 | req = await client.get(f"https://man.archlinux.org/man/{argument}.txt") 27 | text = req.read().decode("utf-8") 28 | if req.status_code == 404: 29 | await linuxman.finish(_lang.text("linuxman.error", [], event.get_user_id())) 30 | try: 31 | await linuxman.finish(text) 32 | except ActionFailed: 33 | text = text.split("\n\n") 34 | msg: List[MessageSegment] = [] 35 | qq = str((await bot.get_login_info())["user_id"]) 36 | nowLen = 0 37 | for t in text: 38 | msg.append( 39 | MessageSegment.node_custom(qq, "XDBOT2 LINUX MAN", t.strip()) 40 | ) 41 | nowLen += len(t) + 1 42 | nowLen = 0 43 | for _ in range(len(msg) // 99 + 1): 44 | nowLen += 99 45 | await bot.call_api( 46 | api="send_group_forward_msg", 47 | messages=msg[nowLen - 99 : nowLen], 48 | group_id=str(event.group_id), 49 | ) 50 | await linuxman.finish( 51 | _lang.text("linuxman.finish", [], event.get_user_id()) 52 | ) 53 | except FinishedException: 54 | raise FinishedException() 55 | except Exception: 56 | await _error.report(traceback.format_exc(), linuxman) 57 | 58 | 59 | # [HELPSTART] Version: 2 60 | # Command: linuxman 61 | # Usage: linuxman:查看linux manpage 62 | # Usage: linuxman man.1:查看man(1) 63 | # Info: 查看linux manpage 64 | # Msg: 查看manpage 65 | # [HELPEND] 66 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/man.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import traceback 4 | from . import _error 5 | from . import _lang 6 | from nonebot import on_command 7 | from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent 8 | from nonebot.exception import FinishedException 9 | from nonebot.params import CommandArg 10 | import os.path 11 | import time 12 | import sys 13 | 14 | sys.path.append(os.path.abspath("src/plugins/Core/lib/md2img")) 15 | markdown2image = __import__("markdown2image") 16 | 17 | man = on_command("man", aliases={"手册", "info"}) 18 | ctrlGroup = json.load(open("data/ctrl.json", encoding="utf-8"))["control"] 19 | 20 | 21 | @man.handle() 22 | async def manHandle(bot: Bot, event: MessageEvent, message: Message = CommandArg()): 23 | try: 24 | argument = message.extract_plain_text() 25 | if argument == "": 26 | with open("docs/README.md", encoding="utf-8") as f: 27 | text = f.read() 28 | # 去换行 29 | while True: 30 | if text[-1] == "\n": 31 | text = text[:-1] 32 | else: 33 | break 34 | else: 35 | command = re.search(r"[A-Za-z\-_]+", argument) 36 | page = re.search(r"[0-9]+", argument) or "0" 37 | if command: 38 | command = command.group(0) 39 | if page and not isinstance(page, str): 40 | page = page.group(0) 41 | # 读取文件 42 | with open(f"man/{command}/{page}.md", encoding="utf-8") as f: 43 | text = f.read() 44 | # 去末尾换行 45 | while True: 46 | if text[-1] == "\n": 47 | text = text[:-1] 48 | else: 49 | break 50 | # 发送 51 | filename = f"data/man.cache_{time.time()}.ro.png" 52 | markdown2image.md2img(text, filename) 53 | _text = text.replace("\n", " \n").replace("#", " ").replace("`", " ") 54 | await man.send( 55 | Message(f"[CQ:image,file=file://{os.path.abspath(filename)}]{_text}") 56 | ) 57 | os.remove(filename) 58 | 59 | except FinishedException: 60 | raise FinishedException() 61 | except FileNotFoundError: 62 | await man.finish(_lang.text("man.error", [], event.get_user_id())) 63 | except Exception: 64 | await _error.report(traceback.format_exc(), man) 65 | 66 | 67 | # [HELPSTART] Version: 2 68 | # Command: man 69 | # Usage man [章节] [页面]:查看手册指定章节 70 | # Info XDbot2 内置的使用参考手册 71 | # Msg: XDbot2 使用手册 72 | # [HELPEND] 73 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/md2img.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | import traceback 4 | from . import _error 5 | from . import _lang 6 | from nonebot import on_command 7 | from nonebot.adapters.onebot.v11 import Bot, Message, MessageEvent 8 | from nonebot.exception import FinishedException 9 | from nonebot.params import CommandArg 10 | import os.path 11 | import time 12 | import sys 13 | 14 | sys.path.append(os.path.abspath("src/plugins/Core/lib/md2img")) 15 | markdown2image = __import__("markdown2image") 16 | md2img = on_command("md2img", aliases={"markdown渲染", "md渲染"}) 17 | 18 | 19 | @md2img.handle() 20 | async def md2imgHandle(bot: Bot, event: MessageEvent, message: Message = CommandArg()): 21 | try: 22 | markdown = message.extract_plain_text() 23 | # 发送 24 | filename = f"data/md2img.cache_{time.time()}.ro.png" 25 | markdown2image.md2img(markdown, filename) 26 | await md2img.send( 27 | Message(f"[CQ:image,file=file://{os.path.abspath(filename)}]") 28 | ) 29 | os.remove(filename) 30 | 31 | except FinishedException: 32 | raise FinishedException() 33 | except Exception: 34 | await _error.report(traceback.format_exc()) 35 | 36 | 37 | # [HELPSTART] Version: 2 38 | # Command: md2img 39 | # Usage: md2img <内容> 40 | # Info: Markdown转图片(还在测试中,如果部分控件没有渲染或渲染不正确就是没写完) 41 | # Msg: MD转图片 42 | # [HELPEND] 43 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/pawcoin.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_regex 2 | from .etm import bag, economy, user 3 | from .etm.item import Item 4 | from ._utils import * 5 | 6 | 7 | async def usePawCoin(user_id: str, count: int = 1) -> None: 8 | items: list[Item] = bag.get_user_bag(user_id) 9 | for i in range(len(items)): 10 | item = items[i] 11 | if item.item_id == "pawcoin": 12 | if item.count >= count: 13 | item.count -= count 14 | break 15 | else: 16 | count -= item.count 17 | item.count = 0 18 | if count == 0: 19 | break 20 | else: 21 | config = Json("pawcoin.config.json")[user_id] 22 | if user.get_user_data(user_id)["vimcoin"] >= count * 3 and config in [ 23 | True, 24 | None, 25 | ]: 26 | economy.use_vi(user_id, count * 3) 27 | else: 28 | raise NoPawCoinException 29 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/pay.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from ._utils import * 3 | from . import _lang as lang 4 | from .etm import economy, user 5 | import traceback 6 | 7 | pay = on_command("pay", aliases={"转账"}) 8 | 9 | # [HELPSTART] Version: 2 10 | # Command: pay 11 | # Usage: pay <QQ号> <金额> 12 | # Info: 给别人转账 13 | # [HELPEND] 14 | 15 | 16 | @pay.handle() 17 | async def handle_pay_command(event: MessageEvent, message: Message = CommandArg()): 18 | try: 19 | argument = str(message).split(" ") 20 | qq = int(argument[0].replace("[CQ:at,qq=", "").replace("]", "")) 21 | num = float(argument[1]) 22 | src_qq = event.get_user_id() 23 | if not economy.use_vimcoin(src_qq, num): 24 | await pay.finish(lang.text("pay.vim_not_enough", [], src_qq)) 25 | economy.add_vi(qq, num) 26 | await pay.finish(lang.text("pay.sucess", [], src_qq)) 27 | except ValueError: 28 | await pay.finish(lang.text("pay.failed", [], event.user_id)) 29 | except BaseException: 30 | await error.report(traceback.format_exc()) 31 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/poke.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | import traceback 3 | from nonebot import on_command 4 | import asyncio 5 | from nonebot.adapters.onebot.v11 import Message 6 | from . import _error 7 | from nonebot.matcher import Matcher 8 | from nonebot.params import CommandArg 9 | 10 | 11 | @on_command("poke").handle() 12 | async def poke(matcher: Matcher, message: Message = CommandArg()): 13 | try: 14 | argv = message.extract_plain_text().split(" ") 15 | for _ in range(min(15, int(argv[1]))): 16 | await matcher.send(Message(f"[CQ:poke,qq={argv[0]}]")) 17 | await asyncio.sleep(random()) 18 | except BaseException: 19 | await _error.report(traceback.format_exc(), matcher) 20 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/random_number.py: -------------------------------------------------------------------------------- 1 | import random 2 | from nonebot.exception import FinishedException 3 | from nonebot import on_command 4 | from nonebot.params import CommandArg 5 | from nonebot.adapters.onebot.v11 import Bot, MessageEvent 6 | from nonebot.adapters.onebot.v11.message import Message 7 | from . import _error as error 8 | from . import _lang as lang 9 | import traceback 10 | 11 | random_plugin = on_command("random", aliases={"rd", "随机数"}) 12 | 13 | # [HELPSTART] Version: 2 14 | # Command: random 15 | # Usage: random 16 | # Usage: random <end> 17 | # Usage: random <start> <end> 18 | # Msg: 取随机数 19 | # Info: 在<start>与<end>之间随机取整数,如果不传入参数,则返回0到1的随机小数;如果只传入<end>参数,则从0~<end>随机取数 20 | # [HELPEND] 21 | 22 | 23 | @random_plugin.handle() 24 | async def random_handle(event: MessageEvent, message: Message = CommandArg()): 25 | try: 26 | # 获取参数 27 | args = str(message).strip() 28 | if not args: 29 | # 如果没有参数,默认返回0到1的随机小数 30 | result = random.random() 31 | else: 32 | # 如果有参数,按照空格拆分,第一个参数为随机数范围的下限,第二个参数为随机数范围的上限 33 | arg_list = args.split() 34 | if len(arg_list) == 1: 35 | result = random.randint(0, int(arg_list[0])) 36 | elif len(arg_list) == 2: 37 | result = random.randint(int(arg_list[0]), int(arg_list[1])) 38 | else: 39 | # 参数错误 40 | await random_plugin.finish( 41 | lang.text("random_number.argerr", [], event.get_user_id()) 42 | ) 43 | # 返回结果 44 | await random_plugin.finish(str(result)) 45 | except FinishedException: 46 | raise FinishedException() 47 | except BaseException: 48 | await error.report(traceback.format_exc(), random_plugin) 49 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/reply_probability.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from nonebot.permission import SuperUser 3 | 4 | 5 | @create_command("reply-probability", {"reply-prob"}) 6 | async def _(bot: Bot, event: MessageEvent, message: Message) -> None: 7 | argv = message.extract_plain_text().split(" ") 8 | match argv[0]: 9 | case "group-probability" | "group": 10 | group_id = str(await get_group_id(event)) 11 | if len(argv) == 1: 12 | await finish( 13 | "reply_probability.group_info", 14 | [Json("reply/config/group_probability.json").get(group_id, 1)], 15 | event.user_id, 16 | ) 17 | elif ( 18 | event.sender.role not in ["owner", "admin"] 19 | or event.get_user_id() not in bot.config.superusers 20 | ): 21 | await finish("reply_probability.403", [], event.user_id) 22 | Json("reply/config/group_probability.json")[group_id] = float(argv[1]) 23 | await finish(get_currency_key("ok"), [], event.user_id) 24 | case "": 25 | await finish( 26 | "reply_probability.info", 27 | [Json("reply/config/probability.json").get(event.get_user_id(), 1)], 28 | event.user_id, 29 | ) 30 | case _: 31 | Json("reply/config/probability.json")[event.get_user_id()] = float(argv[0]) 32 | await finish(get_currency_key("ok"), [], event.user_id) 33 | 34 | 35 | # [HELPSTART] Version: 2 36 | # Command: reply-prob 37 | # Msg: 调教触发概率 38 | # Info: 调整调教触发概率(概率参数均为小数,<=1,为 0 为关闭) 39 | # Usage: reply-prob group <概率>:调整当前群聊的触发概率 40 | # Usage: reply-prob <概率>:调整对自己的触发概率 41 | # Usage: reply-prob group:查看当前群聊的触发概率 42 | # Usage: reply-prob:查看对自己的触发概率 43 | # [HELPEND] 44 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/report.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from nonebot.adapters.onebot.v11 import Message, MessageEvent 3 | from nonebot.params import CommandArg 4 | from . import _lang 5 | from . import _error 6 | 7 | report = on_command("report", aliases={"举报", "反馈"}) 8 | 9 | 10 | @report.handle() 11 | async def handle(event: MessageEvent, message: Message = CommandArg()): 12 | await _error.report("「反馈信息」\n" f"{message}\n" f"{event.get_session_id()}") 13 | await report.finish( 14 | _lang.text("report.success", [], event.get_user_id()), at_sender=True 15 | ) 16 | 17 | 18 | # [HELPSTART] Version: 2 19 | # Command: report 20 | # Usage: /report <messages> 21 | # Msg: 举报 & 反馈 22 | # Info: 反馈信息举报其他用户(或自己)的违规行为 23 | # [HELPEND] 24 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/search.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | from nonebot.adapters.onebot.v11.message import MessageSegment 3 | from playwright.async_api import async_playwright 4 | import asyncio 5 | import time 6 | 7 | 8 | # [HELPSTART] Version: 2 9 | # Command: search 10 | # Info: 在 Bing 上搜索 11 | # Msg: 必应搜索 12 | # Usage: search <content> 13 | # [HELPEND] 14 | 15 | 16 | @create_command("search", aliases={"bing"}) 17 | async def handle_search_command( 18 | bot: Bot, event: MessageEvent, message: Message, matcher: Matcher = Matcher() 19 | ): 20 | url = "https://www.bing.com/search?q=" + message.extract_plain_text().replace( 21 | " ", "+" 22 | ) 23 | file_name = f"preview.image_{int(time.time())}.ro" 24 | async with async_playwright() as p: 25 | browser = await p.chromium.launch() 26 | page = await browser.new_page() 27 | await page.goto(url) 28 | await asyncio.sleep(5) 29 | url = page.url 30 | await page.screenshot(path=f"data/{file_name}.png", full_page=True) 31 | await browser.close() 32 | await matcher.finish( 33 | Message( 34 | MessageSegment.image( 35 | file=f"file://{os.path.abspath(os.path.join('./data', f'{file_name}.png'))}" 36 | ) 37 | ) 38 | ) 39 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/send_email.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import os 4 | import time 5 | from .account import multiAccountData 6 | from .etm import data 7 | from nonebot import get_bot, get_bots 8 | 9 | 10 | async def submit_email(mail_data): 11 | users = os.listdir("data/etm") 12 | # 收集数据 13 | _user = [] 14 | for rule in mail_data["rules"]: 15 | if rule[0] == "group": 16 | try: 17 | bot = get_bot(multiAccountData[rule[1]]) 18 | except BaseException: 19 | bot = list(get_bots().values())[0] 20 | group_member_list = await bot.get_group_member_list(group_id=rule[1]) 21 | for user in group_member_list: 22 | _user.append(str(user["user_id"])) 23 | # print(_user) 24 | 25 | # 比对用户 26 | for user_id in users: 27 | if os.path.isdir(os.path.join("data", "etm", user_id)): 28 | user_can_receive = True 29 | for rule in mail_data["rules"]: 30 | match rule[0]: 31 | case "group": 32 | if user_id not in _user: 33 | user_can_receive = False 34 | break 35 | case "user": 36 | if user_id != rule[1]: 37 | user_can_receive = False 38 | break 39 | case "bot": 40 | pass # TODO 按Bot筛选用户 41 | if user_id not in data.emails.keys(): 42 | data.emails[user_id] = [] 43 | if user_can_receive: 44 | data.emails[user_id].append(mail_data["id"]) 45 | 46 | 47 | async def send_email( 48 | receive: str, subject: str, message: str, items: list = [], **params 49 | ) -> str: 50 | data = json.load(open("data/su.mails.json", encoding="utf-8")) 51 | mail_id = hashlib.sha1(str(time.time()).encode("utf-8")).hexdigest()[:7] 52 | data[mail_id] = { 53 | "message": message, 54 | "subject": subject, 55 | "from": "XDBOT", 56 | "rules": [["user", receive]], 57 | "items": items, 58 | "time": time.time(), 59 | "id": mail_id, 60 | } 61 | data[mail_id].update(params) 62 | json.dump( 63 | data, 64 | open("data/su.mails.json", "w", encoding="utf-8"), 65 | ensure_ascii=False, 66 | indent=4, 67 | ) 68 | await submit_email(data[mail_id]) 69 | return mail_id 70 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/setu_rank.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import Message, MessageEvent 2 | from nonebot.adapters.onebot.v11.bot import Bot 3 | from nonebot.exception import FinishedException 4 | from . import _error as error 5 | from . import _lang as lang 6 | from nonebot import on_command 7 | import traceback 8 | import json 9 | 10 | setu_rank = on_command("st-l", aliases={"setu-rank", "随机涩图排行榜"}) 11 | 12 | # [HELPSTART] Version: 2 13 | # Command: st-l 14 | # Usage: st-l 15 | # Info: 查看全局st-r指令使用排行 16 | # Msg: 随机涩图排行 17 | # [HELPEND] 18 | 19 | 20 | @setu_rank.handle() 21 | async def show_setu_ranking(bot: Bot, event: MessageEvent): 22 | try: 23 | data = json.load(open("data/setu.count.json", encoding="utf-8")) 24 | ranking = [] 25 | for user, count in data.items(): 26 | length = 0 27 | is_insert = False 28 | for item in ranking: 29 | if item["count"] <= count: 30 | ranking.insert(length, {"user": user, "count": count}) 31 | is_insert = True 32 | break 33 | length += 1 34 | if not is_insert: 35 | ranking.append({"user": user, "count": count}) 36 | 37 | now_rank = 0 38 | users = 1 39 | my_data = {"user": str(event.user_id), "count": 0, "rank": "999+"} 40 | max_count = 0 41 | 42 | for i in range(len(ranking)): 43 | if ranking[i]["count"] > max_count: 44 | now_rank += users 45 | users = 0 46 | ranking[i]["rank"] = now_rank 47 | users += 1 48 | if ranking[i]["user"] == str(event.user_id): 49 | my_data = ranking[i] 50 | 51 | reply = lang.text("setu_rank.title", [], str(event.user_id)) 52 | for user in ranking[:12]: 53 | nickname = (await bot.get_stranger_info(user_id=user["user"]))["nickname"] 54 | reply += f"\n{user['rank']}. {nickname}: {user['count']}" 55 | reply += "\n" + "-" * 30 56 | nickname = (await bot.get_stranger_info(user_id=my_data["user"]))["nickname"] 57 | reply += f"\n{my_data['rank']}. {nickname}: {my_data['count']}" 58 | 59 | await setu_rank.finish(reply) 60 | 61 | except FinishedException: 62 | raise FinishedException() 63 | except BaseException: 64 | await error.report(traceback.format_exc(), setu_rank) 65 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/sign.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command, on_regex 2 | 3 | from ._sign import _sign 4 | from . import _error as error 5 | from nonebot.adapters.onebot.v11.event import MessageEvent 6 | import traceback 7 | 8 | 9 | sign = on_regex("^(签到|.sign)$") 10 | sign_rank = on_command("sign-rank") 11 | 12 | 13 | @sign.handle() 14 | async def sign_handler(event: MessageEvent): 15 | try: 16 | await sign.finish(_sign(event.get_user_id())) 17 | except BaseException: 18 | await error.report(traceback.format_exc(), sign) 19 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su.py: -------------------------------------------------------------------------------- 1 | from typing import Awaitable, Callable, Optional, TypeAlias, TypedDict 2 | from ._utils import * 3 | 4 | HANDLE_FUNC: TypeAlias = Callable[[Bot, MessageEvent, Message], Awaitable[None]] 5 | 6 | 7 | class HandlerData(TypedDict): 8 | names: list[str] 9 | function: HANDLE_FUNC 10 | 11 | 12 | su: type[Matcher] = on_command("su", aliases={"超管"}, permission=SUPERUSER) 13 | handlers: list[HandlerData] = [] 14 | 15 | 16 | def create_superuser_command( 17 | name: str, aliases: set[str] = set() 18 | ) -> Callable[..., HANDLE_FUNC]: 19 | """ 20 | 注册 su 指令 21 | 22 | Args: 23 | name (str): 指令名称 24 | aliases (set[str], optional): 别名. Defaults to set(). 25 | 26 | Returns: 27 | Callable[..., HANDLE_FUNC]: 触发器 28 | """ 29 | 30 | def _(func: HANDLE_FUNC) -> HANDLE_FUNC: 31 | handlers.append({"names": [name] + list(aliases), "function": func}) 32 | logger.success(f"成功注册超管指令: {name}") 33 | return func 34 | 35 | return _ 36 | 37 | 38 | def get_handler_function(name: str) -> Optional[HANDLE_FUNC]: 39 | """ 40 | 通过名称获取处理函数 41 | 42 | Args: 43 | name (str): 子命令 44 | 45 | Returns: 46 | Optional[HANDLE_FUNC]: 函数 47 | """ 48 | for handler in handlers: 49 | if name in handler["names"]: 50 | return handler["function"] 51 | 52 | 53 | @su.handle() 54 | async def _(bot: Bot, event: MessageEvent, message: Message = CommandArg()) -> None: 55 | if not message or message[0].type != "text": 56 | await finish("su.need_argv", [], event.user_id) 57 | sub_command: str = message[0].data["text"].split(" ")[0] 58 | logger.debug(f"[SU] 子命令: {sub_command}") 59 | if not (func := get_handler_function(sub_command)): 60 | return 61 | message[0].data["text"] = message[0].data["text"][len(sub_command) :].strip() 62 | if not message[0].data["text"]: 63 | message.pop(0) 64 | try: 65 | await func(bot, event, message) 66 | except Exception: 67 | await error.report() 68 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_call.py: -------------------------------------------------------------------------------- 1 | import json 2 | from nonebot.adapters.onebot.v11 import Bot 3 | from nonebot.adapters.onebot.v11 import Message 4 | from nonebot.params import CommandArg 5 | from .su import su 6 | from . import _error 7 | import traceback 8 | 9 | 10 | @su.handle() 11 | async def call_api(bot: Bot, message: Message = CommandArg()): 12 | argument = str(message).split(" ") 13 | try: 14 | if argument[0] in ["call", "调用"]: 15 | await su.finish( 16 | json.dumps( 17 | await bot.call_api( 18 | api=argument[1], **json.loads(" ".join(argument[2:])) 19 | ) 20 | ) 21 | ) 22 | 23 | except BaseException: 24 | await _error.report(traceback.format_exc(), su) 25 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_config.py: -------------------------------------------------------------------------------- 1 | from nonebot.params import CommandArg 2 | from nonebot.adapters.onebot.v11 import Message 3 | from .su import su 4 | from . import _error 5 | import json 6 | import traceback 7 | 8 | 9 | @su.handle() 10 | async def set_config(message: Message = CommandArg()): 11 | argument = str(message).split(" ") 12 | try: 13 | if argument[0] in ["config", "配置"]: 14 | if argument[1] in ["set-key", "设置键"]: 15 | config = json.load(open(f"data/{argument[2]}", encoding="utf-8")) 16 | config[json.loads(argument[3])] = " ".join(argument[4:]) 17 | json.dump(config, open(f"data/{argument[2]}", "w", encoding="utf-8")) 18 | 19 | await su.finish( 20 | f"{argument[2]}::{argument[3]} -> {' '.join(argument[4:])}" 21 | ) 22 | elif argument[1] in ["set", "设置"]: 23 | with open(f"data/{argument[2]}", "w", encoding="utf-8") as f: 24 | f.write(" ".join(argument[3:])) 25 | await su.finish(f"{argument[2]} -> {' '.join(argument[3:])}") 26 | except BaseException: 27 | await _error.report(traceback.format_exc(), su) 28 | 29 | 30 | @su.handle() 31 | async def get_config(message: Message = CommandArg()): 32 | argument = str(message).split(" ") 33 | try: 34 | if argument[0] in ["config", "配置"]: 35 | if argument[1] in ["get", "获取"]: 36 | if len(argument) >= 4: 37 | await su.finish( 38 | str( 39 | json.dumps( 40 | json.load( 41 | open(f"data/{argument[2]}", encoding="utf-8") 42 | )[json.loads(" ".join(argument[3:]))] 43 | ) 44 | ) 45 | ) 46 | else: 47 | with open(f"data/{argument[2]}", encoding="utf-8") as f: 48 | await su.finish(f.read()) 49 | except BaseException: 50 | await _error.report(traceback.format_exc(), su) 51 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_ct.py: -------------------------------------------------------------------------------- 1 | from nonebot.params import CommandArg 2 | from .su import su 3 | import traceback 4 | import os 5 | from . import _error 6 | from nonebot.adapters.onebot.v11 import Message 7 | import json 8 | 9 | 10 | @su.handle() 11 | async def ct(message: Message = CommandArg()): 12 | argument = str(message).split(" ") 13 | try: 14 | if argument[0] == "ct" or argument[0] == "发言排名": 15 | if argument[1] == "clear" or argument[1] == "清除数据": 16 | fileList = os.listdir("data") 17 | for file in fileList: 18 | if file.startswith("ct."): 19 | json.dump(dict(), open(f"data/{file}", "w", encoding="utf-8")) 20 | await su.send(f"已重置:{file}") 21 | await su.finish("已清除所有发言排名数据") 22 | except BaseException: 23 | await _error.report(traceback.format_exc(), su) 24 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_echo.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import Message 2 | from nonebot.params import CommandArg 3 | from .su import su 4 | from traceback import format_exc 5 | from . import _error as error 6 | from nonebot.adapters.onebot.v11 import Message 7 | 8 | 9 | @su.handle() 10 | async def echo(message: Message = CommandArg()): 11 | argument = str(message).split(" ") 12 | try: 13 | if argument[0] in ["echo", "调试输出"]: 14 | await su.send( 15 | Message( 16 | ("".join(argument[1:]).replace("[", "[").replace("]", "]")) 17 | ) 18 | ) 19 | except BaseException: 20 | await error.report(format_exc(), su) 21 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_forward.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from nonebot.adapters.onebot.v11 import Bot 3 | from nonebot.adapters.onebot.v11 import Message 4 | from nonebot.matcher import Matcher 5 | from nonebot.params import CommandArg 6 | from .su import su 7 | from . import _error 8 | from . import forward 9 | import json 10 | 11 | 12 | @su.handle() 13 | async def set_forward(matcher: Matcher, bot: Bot, message: Message = CommandArg()): 14 | try: 15 | argument = message.extract_plain_text().split(" ") 16 | if argument[0] == "forward" or argument[0] == "消息转发": 17 | data = json.load(open("data/forward.groupList.json", encoding="utf-8")) 18 | if argument[1] == "add" or argument[1] == "添加群": 19 | data += [argument[2]] 20 | try: 21 | await bot.set_group_card( 22 | group_id=int(argument[2]), 23 | user_id=(await bot.get_login_info())["user_id"], 24 | card=(await bot.get_login_info())["nickname"] + "(监听中)", 25 | ) 26 | except BaseException: 27 | pass 28 | elif argument[1] == "remove" or argument[1] == "删除群": 29 | length = 0 30 | for group in data: 31 | if group == argument[2]: 32 | data.pop(length) 33 | try: 34 | await bot.set_group_card( 35 | group_id=int(argument[2]), 36 | user_id=(await bot.get_login_info())["user_id"], 37 | card=(await bot.get_login_info())["nickname"][:-5], 38 | ) 39 | except BaseException: 40 | pass 41 | 42 | else: 43 | length += 1 44 | await matcher.send("完成") 45 | json.dump(data, open("data/forward.groupList.json", "w", encoding="utf-8")) 46 | forward.forwardData = json.load( 47 | open("data/forward.groupList.json", encoding="utf-8") 48 | ) 49 | 50 | except BaseException: 51 | await _error.report(traceback.format_exc(), matcher) 52 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_give.py: -------------------------------------------------------------------------------- 1 | from .su import su 2 | from . import _error 3 | from nonebot.adapters import Message 4 | from nonebot.params import CommandArg 5 | from .etm import bag 6 | 7 | try: 8 | json = __import__("json5") 9 | except: 10 | import json 11 | 12 | 13 | @su.handle() 14 | async def _(message: Message = CommandArg()): 15 | try: 16 | args = str(message).strip().split(" ") 17 | if args[0] in ["give", "给"]: 18 | bag.add_item( 19 | args[1].replace("[CQ:at,qq=", "").replace("]", ""), 20 | args[2], 21 | int(args[3]) if len(args) >= 4 and args[3][0] != "{" else 1, 22 | json.loads(" ".join(args[4:]) if len(args) >= 5 else "{}"), 23 | ) 24 | await su.finish("完成!") 25 | except: 26 | await _error.report() 27 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_notice.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from nonebot.params import CommandArg 3 | from nonebot.adapters.onebot.v11 import Message 4 | from .su import su 5 | from . import _error 6 | from nonebot import get_bots 7 | import json 8 | 9 | su_notice_cache = "" 10 | 11 | 12 | @su.handle() 13 | async def su_primary_notice(message: Message = CommandArg()): 14 | try: 15 | argument = str(message).split(" ") 16 | if ( 17 | argument[0] == "notice" 18 | or argument[0] == "超级广播" 19 | or argument[0] == "广播" 20 | ): 21 | global su_notice_cache 22 | text = str(message)[argument[0].__len__() + 1 :] 23 | if text == "submit": 24 | if su_notice_cache != "": 25 | multiAccoutData = json.load( 26 | open("data/su.multiaccoutdata.ro.json", encoding="utf-8") 27 | ) 28 | groupList = list(multiAccoutData.keys()) 29 | bots = get_bots() 30 | # 开始广播 31 | for group in groupList: 32 | try: 33 | await bots[multiAccoutData[group]].send_group_msg( 34 | message=Message(f"【超级广播】\n{su_notice_cache}"), 35 | group_id=group, 36 | ) 37 | except BaseException: 38 | await su.send( 39 | f"在 {group} 广播消息失败:\n{traceback.format_exc()}" 40 | ) 41 | su_notice_cache = "" 42 | else: 43 | await su.finish("请先使用 /su notice <context> 设定超级广播内容") 44 | elif text == "drop": 45 | su_notice_cache = "" 46 | await su.finish("超级广播内容已清除") 47 | elif text == "get": 48 | await su.finish(Message(su_notice_cache)) 49 | else: 50 | su_notice_cache = text 51 | await su.finish("超级广播内容已设定") 52 | except BaseException: 53 | await _error.report(traceback.format_exc(), su) 54 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_old_reply.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import MessageEvent 2 | from nonebot.matcher import Matcher 3 | from nonebot.adapters import Message 4 | from nonebot.params import CommandArg 5 | from .su import su 6 | from . import _smart_reply as smart_reply 7 | from . import _error 8 | 9 | 10 | @su.handle() 11 | async def _(matcher: Matcher, event: MessageEvent, message: Message = CommandArg()): 12 | try: 13 | argument = str(message).split(" ") 14 | if argument[0] in ["old-reply", "old-调教"]: 15 | if argument[1] in ["remove", "rm", "删除"]: 16 | smart_reply.remove_reply(argument[2], event.get_user_id(), True) 17 | await matcher.finish("完成") 18 | except: 19 | await _error.report() 20 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_plugin.py: -------------------------------------------------------------------------------- 1 | from nonebot.params import CommandArg 2 | from .su import su 3 | from . import _error 4 | import traceback 5 | import json 6 | from nonebot.adapters import Message 7 | 8 | 9 | @su.handle() 10 | async def su_plugin(message: Message = CommandArg()): 11 | try: 12 | argument = str(message).split(" ") 13 | if argument[0] in ["plugins", "插件管理", "plugin"]: 14 | config = json.load(open("data/init.disabled.json", encoding="utf-8")) 15 | if argument[1] == "disable" or argument[1] == "禁用": 16 | if argument[2] not in config: 17 | config += [argument[2]] 18 | await su.send("完成") 19 | elif argument[1] == "enable" or argument[1] == "启用": 20 | length = 0 21 | for conf in config: 22 | if conf == argument[2]: 23 | config.pop(length) 24 | break 25 | await su.send("完成") 26 | json.dump(config, open("data/init.disabled.json", "w", encoding="utf-8")) 27 | except BaseException: 28 | await _error.report(traceback.format_exc(), su) 29 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_restart.py: -------------------------------------------------------------------------------- 1 | from nonebot.params import CommandArg 2 | from traceback import format_exc 3 | from .su import su 4 | from .etm import data 5 | from . import _error 6 | from nonebot.adapters.onebot.v11 import Message 7 | 8 | import sys 9 | import os 10 | 11 | 12 | def _restart() -> None: 13 | script = sys.argv[0] 14 | args = sys.argv[1:] 15 | os.execv(sys.executable, [sys.executable] + [script] + args) 16 | 17 | 18 | @su.handle() 19 | async def restart(message: Message = CommandArg()): 20 | argument = str(message).split(" ") 21 | try: 22 | # 保存数据 23 | data.save_data() 24 | if argument[0] in ["restart", "重新启动"]: 25 | # with open("data/reboot.py", "w") as f: 26 | # f.write(str(time.time())) 27 | await su.send("重启命令已发出") 28 | _restart() 29 | except BaseException: 30 | await _error.report(format_exc(), su) 31 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_screenshot.py: -------------------------------------------------------------------------------- 1 | from nonebot.adapters.onebot.v11 import Message 2 | from nonebot.params import CommandArg 3 | from .su import su 4 | import traceback 5 | from . import _error 6 | import os.path 7 | from nonebot.log import logger 8 | 9 | try: 10 | import pyautogui 11 | except BaseException: 12 | logger.warning("可选依赖 pyautogui 未安装") 13 | 14 | 15 | @su.handle() 16 | async def screenshot(message: Message = CommandArg()): 17 | argument = str(message).split(" ") 18 | try: 19 | if argument[0] in ["截图", "screenshot"]: 20 | try: 21 | os.remove("data/screenshot.png") 22 | except BaseException: 23 | pass 24 | try: 25 | pyautogui.screenshot("data/screenshot.png") 26 | except NameError: 27 | await su.send("错误:可选依赖 pyautogui 未安装") 28 | except OSError: 29 | await su.send("失败:无法截图") 30 | else: 31 | await su.send( 32 | Message( 33 | f"[CQ:image,file=file://{os.path.abspath('./data/screenshot.png')}]" 34 | ) 35 | ) 36 | except BaseException: 37 | await _error.report(traceback.format_exc(), su) 38 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/su_update.py: -------------------------------------------------------------------------------- 1 | from .etm import data 2 | from nonebot.params import CommandArg 3 | from .su import su 4 | from . import _error as error 5 | import traceback 6 | import os 7 | from .su_restart import _restart 8 | from nonebot.adapters.onebot.v11 import Message 9 | 10 | 11 | @su.handle() 12 | async def update(message: Message = CommandArg()): 13 | argument = str(message).split(" ") 14 | try: 15 | if argument[0] in ["update", "检查更新"]: 16 | data.save_data() 17 | await su.send("正在运行更新程序,请稍候 ...") 18 | await su.finish(os.popen("git pull").read()) 19 | elif argument[0] in ["upgrade", "升级"]: 20 | data.save_data() 21 | await su.send("正在更新,请稍候 ...") 22 | await su.send(os.popen("git pull").read()) 23 | _restart() 24 | except BaseException: 25 | await error.report(traceback.format_exc(), su) 26 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/unread_email_reminder.py: -------------------------------------------------------------------------------- 1 | from . import _error, _lang 2 | from .etm import data 3 | from .sign import sign 4 | from .userinfo import panel 5 | 6 | 7 | from nonebot.adapters.onebot.v11 import MessageEvent 8 | from nonebot.matcher import Matcher 9 | 10 | 11 | import json 12 | import traceback 13 | 14 | 15 | @panel.handle() 16 | @sign.handle() 17 | async def unread_email_reminder(matcher: Matcher, event: MessageEvent): 18 | try: 19 | reminded_data = json.load(open("data/email.reminded.json", encoding="utf-8")) 20 | if data.emails.get(event.get_user_id()): 21 | email_count = 0 22 | for email in data.emails[event.get_user_id()]: 23 | if email not in (reminded_data.get(event.get_user_id()) or []): 24 | email_count += 1 25 | if email_count != 0: 26 | await matcher.send( 27 | _lang.text( 28 | "email.remind", 29 | [len(data.emails[event.get_user_id()])], 30 | event.get_user_id(), 31 | ) 32 | ) 33 | reminded_data[event.get_user_id()] = data.emails[event.get_user_id()] 34 | json.dump( 35 | reminded_data, 36 | open("data/email.reminded.json", "w", encoding="utf-8"), 37 | ) 38 | except BaseException: 39 | await _error.report(traceback.format_exc(), matcher) 40 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/use.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from nonebot.adapters.onebot.v11.event import MessageEvent 3 | 4 | # from nonebot.adapters.onebot.v11.bot import Bot 5 | from nonebot.adapters.onebot.v11 import Message 6 | from nonebot.params import CommandArg 7 | from .etm import bag 8 | from . import _error as error 9 | import traceback 10 | 11 | use_cmd = on_command("use", aliases={"使用"}) 12 | 13 | 14 | @use_cmd.handle() 15 | async def use_item(event: MessageEvent, message: Message = CommandArg()): 16 | try: 17 | qq = event.get_user_id() 18 | argv = message.extract_plain_text().split(" ") 19 | await use_cmd.finish( 20 | "\n".join(await bag.use_item(qq, int(argv[0]) - 1, " ".join(argv[1:]))) 21 | ) 22 | except ValueError: 23 | pass 24 | except BaseException: 25 | await error.report(traceback.format_exc()) 26 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/userinfo.py: -------------------------------------------------------------------------------- 1 | from nonebot import on_command 2 | from nonebot.adapters.onebot.v11.event import MessageEvent 3 | from nonebot.adapters.onebot.v11.bot import Bot 4 | from .etm import user, exp, economy, health 5 | from .etm.data import basic_data 6 | from . import _lang as lang 7 | from . import _error as error 8 | import traceback 9 | 10 | panel = on_command( 11 | "panel", aliases={"mypanel", "我的面板", "我的数据", "我的信息", "userInfo"} 12 | ) 13 | 14 | 15 | @panel.handle() 16 | async def show_panel(bot: Bot, event: MessageEvent): 17 | try: 18 | qq = event.get_user_id() 19 | data = user.get_user_data(qq) 20 | nickname = (await bot.get_stranger_info(user_id=qq))["nickname"] 21 | level = exp.get_user_level(qq) 22 | # level_max_exp = level ** 2 23 | bar_filled = int(exp.get_exp(qq) / (level**2 - (level - 1) ** 2) * 10) 24 | # 获取排行 25 | _data = [] 26 | for u, d in list(basic_data.items()): 27 | _data.append({"user": u, "vimcoin": d["vimcoin"]}) 28 | try: 29 | _data.index({"user": qq, "vimcoin": data["vimcoin"]}) 30 | except ValueError: 31 | _data.append({"user": qq, "vimcoin": data["vimcoin"]}) 32 | _data = sorted(_data, key=lambda x: x["vimcoin"], reverse=True) 33 | _rk = _data.index({"user": qq, "vimcoin": data["vimcoin"]}) 34 | # 发送 35 | await panel.send( 36 | ( 37 | f"{lang.text('userinfo.title', [], qq)}\n" 38 | f"—————————————\n" 39 | f"{nickname}({qq})\n" 40 | f" {lang.text('userinfo.level', [], qq)}:Lv{level} ({int(exp.get_exp(qq))} / {(level)**2 - (level-1)**2} exp)\n" 41 | f" [{'=' * max(bar_filled-1, 0)}>{' ' * (10 - bar_filled)}]\n" 42 | f" {lang.text('userinfo.vimcoin', [], qq)}:{round(data['vimcoin'], 2)}vim (No. {_rk + 1})\n" 43 | f" {lang.text('userinfo.health', [], qq)}:{user.get_hp(event.user_id)} / {health.get_max_hp(event.user_id)}" 44 | ) 45 | ) 46 | 47 | except BaseException: 48 | await error.report(traceback.format_exc(), panel) 49 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/version.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from nonebot import on_command 3 | import os 4 | from ._utils import Json, lang 5 | from . import _error as error 6 | from nonebot.matcher import Matcher 7 | 8 | 9 | @on_command("version", aliases={"版本信息"}).handle() 10 | async def show_version(matcher: Matcher): 11 | try: 12 | git_log = os.popen(r"git log --pretty=format:%B").read().splitlines()[0] 13 | await matcher.finish( 14 | lang.text("version.version", [Json("init.json")["version"], git_log]) 15 | ) 16 | except BaseException: 17 | await error.report(traceback.format_exc(), matcher) 18 | -------------------------------------------------------------------------------- /src/plugins/Core/plugins/whoami.py: -------------------------------------------------------------------------------- 1 | from ._utils import * 2 | 3 | from nonebot.adapters.onebot.v11 import MessageEvent, Message 4 | from nonebot.params import CommandArg 5 | from nonebot import get_driver 6 | 7 | 8 | @create_command("whoami") 9 | async def _(_bot, event: MessageEvent, message: Message = CommandArg()): 10 | args = str(message).split(" ") 11 | if args[0] == "" or len(args) == 0: 12 | await finish( 13 | "whoami.text", 14 | [event.sender.user_id, event.sender.nickname], 15 | event.sender.user_id, 16 | False, 17 | True, 18 | ) 19 | elif args[0] == "detail": 20 | await finish( 21 | "whoami.text.detail", 22 | [ 23 | event.sender.user_id, 24 | event.sender.nickname, 25 | event.sender.sex, 26 | event.sender.age, 27 | event.sender.level, 28 | event.sender.role, 29 | event.sender.title, 30 | ], 31 | event.sender.user_id, 32 | False, 33 | True, 34 | ) 35 | else: 36 | await finish("whoami.findhelp", [], event.sender.user_id, False, True) 37 | 38 | 39 | # [HELPSTART] Version: 2 40 | # Command: whoami 41 | # Info: 获取用户信息。 42 | # Msg: 我是谁? 43 | # Usage: whoami: 获取使用用户的信息。 44 | # [HELPEND] 45 | -------------------------------------------------------------------------------- /src/plugins/Core/synthesis/mysterybox_lv1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "result": [ 4 | { 5 | "id": "mysterybox_lv1", 6 | "count": 1, 7 | "data": {} 8 | } 9 | ], 10 | "required": [ 11 | { 12 | "id": "mysterious_shard", 13 | "count": 32, 14 | "data": {} 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 student_2333 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot.plugin import PluginMetadata, inherit_supported_adapters, require 2 | 3 | require("nonebot_plugin_alconna") 4 | 5 | from . import __main__ as __main__ # noqa: E402 6 | from .config import ConfigClass # noqa: E402 7 | 8 | __version__ = "0.6.0" 9 | __plugin_meta__ = PluginMetadata( 10 | name="PicMCStat", 11 | description="将一个 Minecraft 服务器的 MOTD 信息绘制为一张图片", 12 | usage="使用 motd 指令查看使用帮助", 13 | homepage="https://github.com/lgc-NB2Dev/nonebot-plugin-picmcstat", 14 | type="application", 15 | config=ConfigClass, 16 | supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"), 17 | extra={"License": "MIT", "Author": "student_2333"}, 18 | ) 19 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/__main__.py: -------------------------------------------------------------------------------- 1 | from typing import NoReturn 2 | 3 | from nonebot import logger, on_command, on_regex 4 | from nonebot.adapters import Event as BaseEvent, Message 5 | from nonebot.exception import FinishedException 6 | from nonebot.params import CommandArg 7 | from nonebot.typing import T_State 8 | from nonebot_plugin_alconna.uniseg import UniMessage 9 | 10 | from .config import ShortcutType, config 11 | from .draw import ServerType, draw 12 | 13 | try: 14 | from nonebot.adapters.onebot.v11 import GroupMessageEvent as OB11GroupMessageEvent 15 | except ImportError: 16 | OB11GroupMessageEvent = None 17 | 18 | 19 | async def finish_with_query(ip: str, svr_type: ServerType) -> NoReturn: 20 | try: 21 | ret = await draw(ip, svr_type) 22 | except Exception: 23 | msg = UniMessage("出现未知错误,请检查后台输出") 24 | else: 25 | msg = UniMessage.image(raw=ret) 26 | await msg.send(reply_to=config.mcstat_reply_target) 27 | raise FinishedException 28 | 29 | 30 | motdpe_matcher = on_command( 31 | "motdpe", 32 | aliases={"motdbe", "!motdpe", "!motdpe", "!motdbe", "!motdbe"}, 33 | state={"svr_type": "be"}, 34 | ) 35 | motd_matcher = on_command( 36 | "motd", 37 | aliases={"!motd", "!motd", "motdje", "!motdje", "!motdje"}, 38 | # priority=2, 39 | state={"svr_type": "je"}, 40 | ) 41 | 42 | 43 | @motd_matcher.handle() 44 | @motdpe_matcher.handle() 45 | async def _(state: T_State, arg_msg: Message = CommandArg()): 46 | arg = arg_msg.extract_plain_text().strip() 47 | svr_type: ServerType = state["svr_type"] 48 | await finish_with_query(arg, svr_type) 49 | 50 | 51 | def append_shortcut_handler(shortcut: ShortcutType): 52 | async def rule(event: BaseEvent): # type: ignore[override] 53 | if not OB11GroupMessageEvent: 54 | logger.warning("快捷指令群号白名单仅可在 OneBot V11 适配器下使用") 55 | elif (wl := shortcut.whitelist) and isinstance(event, OB11GroupMessageEvent): 56 | return event.group_id in wl 57 | return True 58 | 59 | async def handler(): 60 | await finish_with_query(shortcut.host, shortcut.type) 61 | 62 | on_regex(shortcut.regex, rule=rule).append_handler(handler) 63 | 64 | 65 | def startup(): 66 | if s := config.mcstat_shortcuts: 67 | for v in s: 68 | append_shortcut_handler(v) 69 | 70 | 71 | startup() 72 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/config.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | from nonebot import get_plugin_config 4 | from pydantic import BaseModel, Field 5 | 6 | from .const import ServerType 7 | 8 | 9 | class ShortcutType(BaseModel): 10 | regex: str 11 | host: str 12 | type: ServerType # noqa: A003 13 | whitelist: Optional[List[int]] = [] 14 | 15 | 16 | class ConfigClass(BaseModel): 17 | mcstat_font: str = "unifont" 18 | mcstat_show_addr: bool = False 19 | mcstat_show_delay: bool = True 20 | mcstat_show_mods: bool = False 21 | mcstat_reply_target: bool = True 22 | mcstat_shortcuts: List[ShortcutType] = Field(default_factory=list) 23 | mcstat_resolve_dns: bool = True 24 | mcstat_query_twice: bool = True 25 | 26 | 27 | config = get_plugin_config(ConfigClass) 28 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/const.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | 3 | from mcstatus.motd.components import Formatting, MinecraftColor 4 | 5 | ServerType = Literal["je", "be"] 6 | 7 | CODE_COLOR = { 8 | "0": "#000000", 9 | "1": "#0000AA", 10 | "2": "#00AA00", 11 | "3": "#00AAAA", 12 | "4": "#AA0000", 13 | "5": "#AA00AA", 14 | "6": "#FFAA00", 15 | "7": "#AAAAAA", 16 | "8": "#555555", 17 | "9": "#5555FF", 18 | "a": "#55FF55", 19 | "b": "#55FFFF", 20 | "c": "#FF5555", 21 | "d": "#FF55FF", 22 | "e": "#FFFF55", 23 | "f": "#FFFFFF", 24 | "g": "#DDD605", 25 | } 26 | STROKE_COLOR = { 27 | "0": "#000000", 28 | "1": "#00002A", 29 | "2": "#002A00", 30 | "3": "#002A2A", 31 | "4": "#2A0000", 32 | "5": "#2A002A", 33 | "6": "#2A2A00", 34 | "7": "#2A2A2A", 35 | "8": "#151515", 36 | "9": "#15153F", 37 | "a": "#153F15", 38 | "b": "#153F3F", 39 | "c": "#3F1515", 40 | "d": "#3F153F", 41 | "e": "#3F3F15", 42 | "f": "#3F3F3F", 43 | "g": "#373501", 44 | } 45 | CODE_COLOR_BEDROCK = {**CODE_COLOR, "g": "#FFAA00"} 46 | STROKE_COLOR_BEDROCK = {**STROKE_COLOR, "g": "#2A2A00"} 47 | STYLE_BBCODE = { 48 | "l": ["[b]", "[/b]"], 49 | "m": ["[del]", "[/del]"], 50 | "n": ["[u]", "[/u]"], 51 | "o": ["[i]", "[/i]"], 52 | } 53 | 54 | ENUM_CODE_COLOR = {MinecraftColor(k): v for k, v in CODE_COLOR.items()} 55 | ENUM_STROKE_COLOR = {MinecraftColor(k): v for k, v in STROKE_COLOR.items()} 56 | ENUM_CODE_COLOR_BEDROCK = {MinecraftColor(k): v for k, v in CODE_COLOR_BEDROCK.items()} 57 | ENUM_STROKE_COLOR_BEDROCK = { 58 | MinecraftColor(k): v for k, v in STROKE_COLOR_BEDROCK.items() 59 | } 60 | ENUM_STYLE_BBCODE = {Formatting(k): v for k, v in STYLE_BBCODE.items()} 61 | 62 | 63 | GAME_MODE_MAP = {"Survival": "生存", "Creative": "创造", "Adventure": "冒险"} 64 | FORMAT_CODE_REGEX = r"§[0-9abcdefgklmnor]" 65 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/res.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from pil_utils import BuildImage 4 | 5 | MODULE_DIR = Path(__file__).parent 6 | RES_DIR = MODULE_DIR / "res" 7 | 8 | GRASS_RES_PATH = RES_DIR / "grass_side_carried.png" 9 | DIRT_RES_PATH = RES_DIR / "dirt.png" 10 | DEFAULT_ICON_PATH = RES_DIR / "default.png" 11 | 12 | GRASS_RES = BuildImage.open(GRASS_RES_PATH) 13 | DIRT_RES = BuildImage.open(DIRT_RES_PATH) 14 | DEFAULT_ICON_RES = BuildImage.open(DEFAULT_ICON_PATH) 15 | -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/res/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/nonebot_plugin_picmcstat/res/default.png -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/res/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/nonebot_plugin_picmcstat/res/dirt.png -------------------------------------------------------------------------------- /src/plugins/nonebot_plugin_picmcstat/res/grass_side_carried.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Moonlark-Dev/XDbot2/2ae4f22878854627ab86906fb4d9652ebff865fa/src/plugins/nonebot_plugin_picmcstat/res/grass_side_carried.png --------------------------------------------------------------------------------