├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 3-question.yml │ ├── 2-feature_request.yml │ └── 1-bug_report.yml ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── stale.yml ├── workflows │ ├── check_syntax.md │ └── codeql.yml ├── dependabot.yml └── copilot-instructions.md ├── data └── programming_info │ ├── python.json │ └── javascript.json ├── README.md ├── .gitignore ├── cogs ├── _template.py ├── _m10s_beta.py ├── slash │ ├── mini_features.py │ └── m10s_messageinfo.py ├── m10s_direct_msg.py ├── nekok500_mee6.py ├── takumi_twinotif.py ├── m10s_bmail.py ├── m10s_partners.py ├── hybrid │ ├── m10s_quick_cmd.py │ └── m10s_help.py ├── apple_foc.py ├── m10s_app_metadata.py ├── pf9_symmetry.py ├── syouma.py ├── P143_jyanken.py ├── apple_misc.py ├── apple_onlinenotif.py ├── m10s_info.py ├── m10s_remainder.py ├── m10s_set_activity_roles.py ├── apple_invite.py ├── m10s_search.py ├── m10s_manage.py ├── m10s_role_panel.py ├── m10s_games.py ├── m10s_other.py ├── m10s_owner.py └── m10s_auth_wiz.py ├── LICENSE ├── requirements.txt ├── apple_util.py ├── checker.py ├── paginator.py ├── l10n.py ├── m10s_util.py └── mido_util.py /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # ref 2 | 3 | * @SinaKitagami/system-administrator -------------------------------------------------------------------------------- /data/programming_info/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | 4 | "before_block": ":\n", 5 | "indent": " ", 6 | "after_line": "\n", 7 | "after_block": "\n", 8 | 9 | "if": "if {0}", 10 | "while": "while {0}", 11 | "for": "for {0} in {1}", 12 | "def": "def {0}({1})", 13 | 14 | "print": "print({0})", 15 | "ask": "{0} = input('{1}')" 16 | } 17 | -------------------------------------------------------------------------------- /data/programming_info/javascript.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "JavaScript", 3 | 4 | "before_block": " {\n", 5 | "indent": " ", 6 | "after_line": ";\n", 7 | "after_block": "}\n", 8 | 9 | "if": "if ({0})", 10 | "while": "while ({0})", 11 | "for": "for (const {0} of {1})", 12 | "def": "const {0} = ({1}) =>", 13 | 14 | "print": "console.log({0})", 15 | "ask": "{0} = prompt('{1}')" 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # program-team 2 | 3 | 思惟奈ちゃんのプログラム部分。Python製、discord.pyを使用。 4 | 5 | ライセンスはMITライセンス。MITライセンスに従えば自由に使用できますが、このままでは動きません。 6 | 7 | チーム☆思惟奈ちゃんメンバー以外からのPull Requestは基本的にMargeしていません。 8 | 9 | 動作環境構築に関しては、[こちらのリンクから]()確認してください。 10 | 11 | チーム☆思惟奈ちゃんメンバー以外の方は、お手数ですが[こちらから]()お問い合わせください。 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.py 2 | meiryo.ttc 3 | README.md 4 | sina_datas.db 5 | musicfile/ 6 | .vscode/ 7 | __pycache__/ 8 | imgs/ 9 | cogtest.py 10 | backup_bot.py 11 | debug.log 12 | bulk_message_delete.txt 13 | cogs/__part_pjsekai_music_select.py 14 | database.py 15 | dbtra.txt 16 | bot-mii-10sPC.py 17 | .gitignore 18 | my_module/dpy_interaction.py 19 | ment.py 20 | .gitignore 21 | trans_db.py 22 | cogs/m10s_api.py 23 | cogs/slash/pjsekai_music_select.py 24 | pjsekai_musics.json 25 | api_cf_ssl/ 26 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ## 📝 変更内容 3 | 4 | このPRでは以下の変更を行いました: 5 | 6 | - (例:〇〇コマンドの追加/修正) 7 | - (例:非同期処理の見直し) 8 | - (例:例外処理・ログ出力の改善) 9 | 10 | ## 👀 レビューしてほしい点 11 | 12 | - (例:非同期処理に問題がないか確認してほしい) 13 | - (例:Optionalの型指定とNoneチェックの妥当性) 14 | - (例:同じ処理が他のCogに重複していないかどうか) 15 | - (例:特定の例外処理のスコープが適切か確認してほしい) 16 | - (例:関数名や変数名の命名が適切か意見がほしい) 17 | 18 | ## 💬 補足 19 | 20 | (例:例外処理の位置を変更しています。既存の影響がないか確認をお願いします。) 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-question.yml: -------------------------------------------------------------------------------- 1 | name: 質問・使い方相談 2 | description: Appの使い方やコードについて質問する 3 | title: "[質問] " 4 | labels: ["question"] 5 | assignees: mii-10, takumi0213 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ❓ **困っていること、知りたいことを具体的に記載してください。** 11 | 12 | - type: textarea 13 | id: question 14 | attributes: 15 | label: 質問内容 16 | description: どの機能/部分についての質問か、詳しく書いてください 17 | placeholder: 例)〇〇コマンドの使い方がわかりません 18 | validations: 19 | required: true 20 | 21 | - type: input 22 | id: context 23 | attributes: 24 | label: 関連するファイルやコマンド(任意) 25 | placeholder: 例)/game コマンド 26 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | このIssueは、長い間活動が見られません。活動が見られないため、自動的に古いIssueとしてマークされています。 14 | もし、これ以上の活動が見られなければ、自動的にクローズされます。 15 | あなたの貢献に感謝いたします。 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 機能追加・改善の提案 2 | description: 新しい機能の提案や改善要望 3 | title: "[提案] " 4 | labels: ["enhancement"] 5 | assignees: mii-10, takumi0213 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | ✨ **追加・改善してほしい機能について教えてください。** 11 | 12 | - type: textarea 13 | id: summary 14 | attributes: 15 | label: 機能の概要 16 | description: どんな機能を追加/改善したいですか? 17 | placeholder: 例)〇〇コマンドにフィルター機能を追加したい 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: reason 23 | attributes: 24 | label: 背景や目的 25 | description: なぜこの機能が必要ですか?具体的な理由があれば記載してください 26 | placeholder: 例)結果が多すぎて探しにくいため 27 | 28 | - type: textarea 29 | id: other 30 | attributes: 31 | label: その他の情報(任意) 32 | placeholder: 参考資料、画面例、似た機能など 33 | -------------------------------------------------------------------------------- /.github/workflows/check_syntax.md: -------------------------------------------------------------------------------- 1 | name: Check Checks 2 | 3 | on: [push] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Python 3.10 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: "3.12" 19 | 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install flake8 pyright 24 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 25 | 26 | - name: Check Typing with pyright 27 | run: pyright 28 | 29 | - name: Lint with flake8 30 | run: flake8 . --count --max-line-length=999 --statistics 31 | 32 | - name: Lint with flake8 33 | run: flake8 . --count --max-line-length=999 --statistics 34 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "09:00" 8 | timezone: "Asia/Tokyo" 9 | rebase-strategy: "auto" 10 | insecure-external-code-execution: deny 11 | labels: ["dependencies"] 12 | commit-message: 13 | prefix: "deps" 14 | include: "scope" 15 | assignees: 16 | - "takumi0213" 17 | open-pull-requests-limit: 10 18 | 19 | - package-ecosystem: "github-actions" 20 | directory: "/" 21 | schedule: 22 | interval: "weekly" 23 | day: "monday" 24 | time: "09:15" 25 | timezone: "Asia/Tokyo" 26 | labels: ["ci", "dependencies"] 27 | commit-message: 28 | prefix: "ci-deps" 29 | include: "scope" 30 | assignees: 31 | - "takumi0213" 32 | open-pull-requests-limit: 10 33 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | ## コミットメッセージまたはPull Requestの説明を生成する際の要件 2 | - 必ず日本語で簡潔に記述してください 3 | 4 | ## レビュー時に確認するポイント 5 | 以下の観点でコードおよびコンテンツをレビューしてください。コメントはすべて日本語で記述してください。 6 | 1. 機能の正当性 7 | - コマンドやイベントの動作は仕様通りか? 8 | - 引数やオプションのエラーハンドリングは適切か? 9 | - discord.py の仕様(async/awaitなど)に準拠しているか? 10 | 2. 非同期処理の正確さ 11 | - async関数でawaitを忘れていないか? 12 | - 同期的にブロッキングする処理を使っていないか? 13 | 3. コードの可読性・拡張性・保守性 14 | - クラスや関数の分割が適切か? 15 | - 重複コードがないか? 16 | - コメントやdocstringが不足していないか? 17 | 4. 型ヒントとデフォルト値 18 | - Optionalやstr | Noneなど、型指定は適切か? 19 | - 可変デフォルト引数の使用ミスがないか? 20 | 5. 例外処理とログ出力 21 | - try-exceptの使い方が適切か? 22 | - 必要なログ出力が行われているか? 23 | 6. ユーティリティの再利用性 24 | - m10s_util.py などの既存関数を活用しているか? 25 | 7. セキュリティやプライバシー 26 | - DMやユーザー情報の扱いが安全か? 27 | - 個人情報がログに出力されていないか? 28 | 8. バグ(不具合)の可能性 29 | - 型エラー・インデックスエラー・None参照の危険がないか? 30 | 9. ロジックの誤り 31 | - 処理の流れが意図したものになっているか? 32 | 10. パフォーマンスの改善余地 33 | - 無駄なループや重複処理はないか? 34 | - 外部APIや重い処理の呼び出しが最小限になっているか? -------------------------------------------------------------------------------- /cogs/_template.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | 7 | import m10s_util as ut 8 | """↑様々な便利コマンド詰め合わせ 9 | ut.ondevicon(Member) 10 | オンライン状況に基づくデバイスアイコンテキストを返す。 11 | ut.getEmbed(title,description,color,(name,value)...) 12 | Embedのお手軽生成。これ使ったのがあるから消そうにも消せない。 13 | await ut.opendm(Member/User) 14 | DMチャンネルを返します。DMチャンネルが存在しないなんてことでは困らせません。 15 | await wait_message_return(ctx,質問するテキスト,←の送信先,待つ時間): 16 | 入力待ちの簡略化。タイムアウトの例外キャッチを忘れずに 17 | ut.get_vmusic(bot,member) 18 | 思惟奈ちゃんの音楽再生機能でそのメンバーがきいている曲を返します。 19 | """ 20 | 21 | 22 | class ClassName(commands.Cog): 23 | 24 | def __init__(self, bot): 25 | self.bot = bot 26 | 27 | async def cog_load(self): 28 | pass 29 | 30 | @commands.hybrid_command(name="pass", description="") 31 | @ut.runnable_check() 32 | @ut.runnable_check_for_appcmd() 33 | async def command(self, ctx, args): 34 | pass 35 | 36 | @commands.Cog.listener() 37 | async def event(self, args): 38 | pass 39 | 40 | 41 | async def setup(bot): 42 | await bot.add_cog(ClassName(bot)) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 思惟奈チーム - TeamSina 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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-bug_report.yml: -------------------------------------------------------------------------------- 1 | name: バグ報告 2 | description: 動作不具合やエラーを報告します 3 | title: "[バグ]: " 4 | labels: ["bug"] 5 | assignees: mii-10, takumi0213 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | 🐞 **不具合の内容と再現手順をできるだけ詳しく記載してください。** 11 | 12 | - type: textarea 13 | id: overview 14 | attributes: 15 | label: 不具合の概要 16 | description: どのような問題が発生しましたか? 17 | placeholder: 例)〇〇コマンドを実行するとエラーになります 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | id: steps 23 | attributes: 24 | label: 再現手順 25 | description: 手順を番号付きで記載してください 26 | placeholder: | 27 | 1. サーバに参加 28 | 2. /botinfo を実行 29 | 3. エラーが発生する 30 | validations: 31 | required: true 32 | 33 | - type: dropdown 34 | id: priority 35 | attributes: 36 | label: 優先度 37 | description: この問題の緊急度・優先度を選択してください 38 | options: 39 | - '' 40 | - 緊急 41 | - 高 42 | - 中 43 | - 低 44 | - 超低 45 | validations: 46 | required: true 47 | 48 | - type: textarea 49 | id: logs 50 | attributes: 51 | label: エラーログ/スクリーンショット(任意) 52 | description: 可能であれば貼り付けてください 53 | placeholder: エラーIDなどを記載してください 54 | -------------------------------------------------------------------------------- /cogs/_m10s_beta.py: -------------------------------------------------------------------------------- 1 | #codiing:utf-8 2 | 3 | import aiohttp 4 | from dateutil.relativedelta import relativedelta as rdelta 5 | import discord 6 | from discord.ext import commands 7 | 8 | import asyncio 9 | 10 | import m10s_util as ut 11 | 12 | from my_module import dpy_interaction as dpyui 13 | 14 | class m10s_beta(commands.Cog): 15 | 16 | def __init__(self, bot:commands.Bot): 17 | self.bot:commands.Bot = bot 18 | 19 | @commands.command() 20 | @ut.runnable_check() 21 | async def tsts(self, ctx): 22 | menu = dpyui.interaction_menu("test_1","選択してください",1,3) 23 | menu.add_option("りくりく","rikuchan","選択項目1") 24 | menu.add_option("りっくん","rikkun","選択項目2") 25 | menu.add_option("みぃてん☆","m10s","選択項目3") 26 | menu.add_option("test","ts","選択項目4") 27 | menu.add_option("lol","lol","選択項目5") 28 | msg = await self.bot.dpyui.send_with_ui(ctx.channel,"> テスト",ui=menu) 29 | cb:dpyui.interaction_menu_callback = await self.bot.wait_for("menu_select", check=lambda icb:icb.custom_id=="test_1" and icb.message.id==msg.id and icb.clicker_id == ctx.author.id) 30 | await cb.edit_with_ui(content=f"選択された項目の内部識別文字列一覧:\n{cb.selected_value}", ui=[]) 31 | 32 | 33 | 34 | 35 | 36 | async def setup(bot): 37 | await bot.add_cog(m10s_beta(bot)) -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL Analysis" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: '43 2 * * 6' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 15 | permissions: 16 | security-events: write 17 | packages: read 18 | actions: read 19 | contents: read 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | include: 25 | - language: python 26 | build-mode: none 27 | - language: actions 28 | build-mode: none 29 | # "actions" は不要であれば削除可 30 | # - language: actions 31 | # build-mode: none 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v6 36 | 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v4 39 | with: 40 | languages: ${{ matrix.language }} 41 | build-mode: ${{ matrix.build-mode }} 42 | queries: +security-extended,security-and-quality 43 | 44 | - name: Manual build (only if needed) 45 | if: matrix.build-mode == 'manual' 46 | shell: bash 47 | run: | 48 | echo 'Replace this section with your actual build commands.' 49 | exit 1 50 | 51 | - name: Perform CodeQL Analysis 52 | uses: github/codeql-action/analyze@v4 53 | with: 54 | category: "/language:${{ matrix.language }}" 55 | -------------------------------------------------------------------------------- /cogs/slash/mini_features.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import Optional 3 | import discord 4 | from discord.ext import commands 5 | 6 | from discord import app_commands 7 | 8 | import json 9 | 10 | import m10s_util as ut 11 | 12 | class mini_features(commands.Cog): 13 | 14 | def __init__(self, bot): 15 | self.bot = bot 16 | 17 | @app_commands.command(name="shutdown") 18 | @app_commands.guilds(discord.Object(id=582545206017261598)) 19 | @ut.runnable_check() 20 | async def bot_exit(interaction:discord.Interaction): 21 | await interaction.response.send_message("自動的に終了されます…", ephemeral=True) 22 | 23 | bot.tree.add_command(bot_exit, guild=discord.Object(id=582545206017261598)) 24 | 25 | @app_commands.context_menu(name="spread spoiler") 26 | @ut.runnable_check() 27 | async def spread_spoiler(interaction:discord.Interaction, message:discord.Message): 28 | await interaction.response.send_message(embed=discord.Embed(title="スポイラー展開", description=message.content.replace("||", ""), color=self.bot.ec), ephemeral=True) 29 | 30 | bot.tree.add_command(spread_spoiler) 31 | 32 | @commands.hybrid_command(description="一文字ずつ隠すスポイラーの文面を作成します。") 33 | @app_commands.describe(text="作成のもとになるテキスト") 34 | @ut.runnable_check() 35 | async def mark_as_spoiler_each_char(self, ctx, *, text:str): 36 | st = "" 37 | for i in text: 38 | st = st+f"\|\|{i}\|\|" 39 | await ctx.send(embed=discord.Embed(title="スポイラー化テキスト",description=f"{st}", color=self.bot.ec), ephemeral=True) 40 | 41 | async def setup(bot): 42 | await bot.add_cog(mini_features(bot)) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohappyeyeballs==2.6.1 2 | aiohttp==3.13.2 3 | aiomysql==0.3.2 4 | aiosignal==1.4.0 5 | aiosqlite==0.22.0 6 | asgiref==3.11.0 7 | astroid==4.0.2 8 | astunparse==1.6.3 9 | async-timeout==5.0.1 10 | attrs==25.4.0 11 | babel==2.17.0 12 | beautifulsoup4==4.14.3 13 | braceexpand==0.1.7 14 | cachetools==6.2.4 15 | certifi==2025.11.12 16 | cffi==2.0.0 17 | chardet==5.2.0 18 | charset-normalizer==3.4.4 19 | click==8.1.8 20 | colorama==0.4.6 21 | dill==0.4.0 22 | discord.py[voice]==2.6.4 23 | Django==6.0 24 | dropbox==12.0.2 25 | frozenlist==1.8.0 26 | google-api-core==2.28.1 27 | google-api-python-client==2.187.0 28 | google-auth==2.45.0 29 | google-auth-httplib2==0.3.0 30 | googleapis-common-protos==1.72.0 31 | gTTS==2.5.4 32 | httplib2==0.31.0 33 | idna==3.11 34 | import_expression==2.2.1.post1 35 | isort==7.0.0 36 | Jinja2==3.1.6 37 | jishaku==2.6.3 38 | MarkupSafe==3.0.3 39 | mccabe==0.7.0 40 | multidict==6.7.0 41 | mutagen==1.47.0 42 | mysql-connector-python==9.5.0 43 | packaging==25.0 44 | pillow==12.0.0 45 | pip-review==1.3.0 46 | platformdirs==4.5.1 47 | ply==3.11 48 | propcache==0.4.1 49 | proto-plus==1.27.0 50 | protobuf==6.33.2 51 | psutil==7.1.3 52 | pyasn1==0.6.1 53 | pyasn1_modules==0.4.2 54 | pycparser==2.23 55 | pylint==4.0.4 56 | PyMySQL==1.1.2 57 | PyNaCl==1.5.0 58 | pyparsing==3.2.5 59 | PyQt5==5.15.11 60 | PyQt5-Qt5==5.15.18 61 | PyQt5_sip==12.17.2 62 | PyScreeze==1.0.1 63 | python-dateutil==2.9.0.post0 64 | pytz==2025.2 65 | requests==2.32.5 66 | rsa==4.9.1 67 | setuptools==80.9.0 68 | six==1.17.0 69 | soupsieve==2.8.1 70 | sqlparse==0.5.5 71 | stone==3.3.1 72 | tabulate==0.9.0 73 | tomlkit==0.13.3 74 | twitter==1.19.6 75 | typed_ast==1.5.5 76 | typing_extensions==4.15.0 77 | uritemplate==4.2.0 78 | urllib3==2.6.2 79 | wheel==0.45.1 80 | Wikidata==0.9.0 81 | wikipedia==1.4.0 82 | yarl==1.22.0 83 | yt-dlp==2025.12.8 84 | -------------------------------------------------------------------------------- /cogs/m10s_direct_msg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands, tasks 5 | import asyncio 6 | from dateutil.relativedelta import relativedelta as rdelta 7 | import traceback 8 | import m10s_util as ut 9 | import textwrap 10 | 11 | import config 12 | import aiohttp 13 | 14 | from discord import app_commands 15 | 16 | class m10s_direct_msg(commands.Cog): 17 | 18 | def __init__(self, bot): 19 | self.bot: commands.Bot = bot 20 | 21 | @commands.command() 22 | @commands.is_owner() 23 | @ut.runnable_check() 24 | async def send_dm(self, ctx, target_id:int, *, content:str): 25 | try: 26 | user = await self.bot.fetch_user(target_id) 27 | except discord.NotFound: 28 | await ctx.author.send("該当ユーザーが見つかりませんでした。") 29 | except discord.HTTPException: 30 | await ctx.author.send("ユーザーの取得に失敗しました。") 31 | else: 32 | m = await ctx.author.send(f"`{str(user)}`に```\n{content}```と送信してもよろしいですか?") 33 | await m.add_reaction("⭕") 34 | await m.add_reaction("❌") 35 | try: 36 | r,u = await self.bot.wait_for("reaction_add", timeout=30, check=lambda r,u:u.id == ctx.author.id and str(r.emoji) in ["⭕","❌"]) 37 | except asyncio.TimeoutError: 38 | await m.edit(content="タイムアウトしました。再度操作を行ってください。") 39 | return 40 | if str(r.emoji) == "⭕": 41 | try: 42 | await user.send(content) 43 | except: 44 | await ctx.author.send("送信に失敗しました。") 45 | else: 46 | await ctx.author.send("送信しました。") 47 | else: 48 | await ctx.author.send("キャンセルしました。") 49 | 50 | 51 | async def setup(bot): 52 | await bot.add_cog(m10s_direct_msg(bot)) -------------------------------------------------------------------------------- /cogs/nekok500_mee6.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import aiohttp 3 | from discord.ext import commands 4 | 5 | from typing import Optional 6 | 7 | from discord import app_commands 8 | 9 | import m10s_util as ut 10 | 11 | 12 | class MEE6(commands.Cog): 13 | def __init__(self, bot): 14 | self.bot = bot 15 | 16 | @commands.hybrid_command(description="「mee6のレベルランキング」を一覧表示します。") 17 | @ut.runnable_check() 18 | @ut.runnable_check_for_appcmd() 19 | @commands.guild_only() 20 | @app_commands.allowed_contexts(guilds=True, dms=False, private_channels=False) 21 | @app_commands.describe(start="最高順位") 22 | @app_commands.describe(end="最低順位") 23 | @commands.cooldown(1, 5, commands.BucketType.guild) 24 | @app_commands.checks.cooldown(1, 5, key=lambda i: i.guild_id) 25 | async def levels(self, ctx, start:Optional[int]=1, end:Optional[int]=10): 26 | start -= 1 27 | async with self.bot.session.get("https://mee6.xyz/api/plugins/levels/leaderboard/{0}".format(ctx.guild.id)) as resp: 28 | js = await resp.json() 29 | if "status_code" in js: 30 | if js["status_code"] == 404: 31 | await ctx.send(embed=discord.Embed(title=await ctx._("cmd-error-t"), description=await ctx._("mee6-notfound"))) 32 | return 33 | 34 | else: 35 | l = [] 36 | for row in js["players"][start:end]: 37 | l.append("{0}: {1}Lv {2}/{3}exp".format(ctx.guild.get_member(row["id"]).name if not ctx.guild.get_member(row["id"]) is None else ( 38 | row["username"] + "#" + row["discriminator"]), row["level"], row["detailed_xp"][0], row["detailed_xp"][1])) 39 | await ctx.send(embed=discord.Embed(title="MEE6 LeaderBoard", description="\n".join(l), color=0x05FF05)) 40 | 41 | 42 | async def setup(bot): 43 | await bot.add_cog(MEE6(bot)) 44 | -------------------------------------------------------------------------------- /cogs/takumi_twinotif.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from os import cpu_count 4 | import discord 5 | from discord.ext import commands,tasks 6 | 7 | import twitter 8 | 9 | 10 | class twinotif(commands.Cog): 11 | 12 | def __init__(self, bot): 13 | self.bot = bot 14 | self.twi = twitter.Twitter(auth=twitter.OAuth(bot.T_Acs_Token, bot.T_Acs_SToken, bot.T_API_key, bot.T_API_SKey)) 15 | self.target = "sina_status" 16 | self.last_id = self.gtwi_fu(self.target)[0] 17 | self.ch = self.bot.get_channel(1035026381853167676) 18 | self.mention = "<@&889157660837048350>" 19 | self.loop_task.start() 20 | 21 | def gtwi_fu(self,uname): 22 | ret = self.twi.statuses.user_timeline(screen_name=uname,count=1) 23 | return (int(ret[0]["id"]), ret[0]) 24 | 25 | @tasks.loop(seconds=10) 26 | async def loop_task(self): 27 | ret = self.gtwi_fu(self.target) 28 | if not self.last_id == ret[0]: 29 | self.last_id = ret[0] 30 | tweet = ret[1] 31 | if tweet["text"].startswith("[Status]"): 32 | embed = discord.Embed(description=tweet["text"], color=int( 33 | tweet["user"]["profile_background_color"], 16)) 34 | embed.set_author(name=f'{tweet["user"]["name"]}(@{tweet["user"]["screen_name"]})', 35 | url=f'https://twitter.com/{tweet["user"]["screen_name"]}', icon_url=tweet["user"]["profile_image_url_https"]) 36 | try: 37 | embed.set_image( 38 | url=tweet["entities"]["media"][0]["media_url_https"]) 39 | except: 40 | pass 41 | embed.add_field(name="Twitterで見る", value=f'https://twitter.com/{tweet["user"]["screen_name"]}/status/{tweet["id"]}') 42 | await self.ch.send(f"{self.mention}",embed=embed) 43 | 44 | async def setup(bot): 45 | await bot.add_cog(twinotif(bot)) 46 | -------------------------------------------------------------------------------- /cogs/m10s_bmail.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | from discord import app_commands 6 | import asyncio 7 | import random 8 | 9 | import m10s_util as ut 10 | 11 | class m10s_bmail(commands.Cog): 12 | 13 | def __init__(self, bot): 14 | self.bot = bot 15 | self.bmaillog = self.bot.get_channel(1206643710788632606) 16 | 17 | @commands.hybrid_command(name="bottle-mail", aliases=["b-mail","bm"], description="ボトルメールをどこかのサーバーに送ることができます。") 18 | @ut.runnable_check() 19 | @ut.runnable_check_for_appcmd() 20 | async def b_mail(self, ctx:commands.Context): 21 | await ctx.send("DMをご確認ください。", ephemeral=True) 22 | dch = await ut.opendm(ctx.author) 23 | schs = [i for i in self.bot.get_all_channels() if i.name.startswith("sbm-")] 24 | try: 25 | msg: discord.Message = await ut.wait_message_return(ctx, "送信するメッセージをここに入力してください。", dch, 30) 26 | sch = random.choice(schs) 27 | #try: 28 | e = ut.getEmbed("ボトルメールリクエスト", msg.clean_content, self.bot.ec, "送信予定先", f"{sch.name}({sch.id}) in {sch.guild.name}({sch.guild.id})", ) 29 | e.set_author(name=f"{ctx.author}({ctx.author.id})", 30 | icon_url=ctx.author.display_avatar.replace(static_format="png").url) 31 | e.set_footer(text=f"{ctx.guild}({ctx.guild.id})", 32 | icon_url=ctx.guild.icon.replace(static_format="png").url) 33 | check_msg:discord.Message = await self.bmaillog.send(embed=e) 34 | await check_msg.add_reaction("✅") 35 | r,u = await self.bot.wait_for("reaction_add", check = lambda r,u: u.id in self.bot.chat_mod and r.message.id == check_msg.id and str(r.emoji) == "✅") 36 | smg = await sch.send(embed=ut.getEmbed("誰かの手紙が流れ着いた…", msg.clean_content, self.bot.ec)) 37 | 38 | #except: 39 | #pass 40 | except asyncio.TimeoutError: 41 | await dch.send("手紙とボトルはどこかへ消えてしまった…") 42 | 43 | 44 | async def setup(bot): 45 | await bot.add_cog(m10s_bmail(bot)) 46 | -------------------------------------------------------------------------------- /cogs/m10s_partners.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | import config as cf 7 | 8 | import m10s_util as ut 9 | 10 | from discord import app_commands 11 | 12 | 13 | class m10s_partner(commands.Cog): 14 | 15 | def __init__(self, bot): 16 | self.bot = bot 17 | self.partner_ids = cf.partner_ids 18 | self.partners = cf.partners 19 | 20 | @commands.hybrid_command(name="partners",description="思惟奈ちゃんのパートナーを表示します。") 21 | @ut.runnable_check() 22 | @ut.runnable_check_for_appcmd() 23 | async def view_partners(self, ctx): 24 | pmax = len(self.partner_ids)-1 25 | page = 0 26 | 27 | async def get_page(page): 28 | return discord.Embed(title=f"思惟奈ちゃんパートナー:{await self.bot.fetch_user(self.partner_ids[page])}のご紹介", 29 | description=self.partners[page], 30 | color=self.bot.ec) 31 | 32 | msg = await ctx.send(embed=await get_page(page)) 33 | await msg.add_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041)) 34 | await msg.add_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216)) 35 | 36 | while True: 37 | try: 38 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.message.author.id, timeout=30) 39 | except: 40 | break 41 | try: 42 | await msg.remove_reaction(r, u) 43 | except: 44 | pass 45 | if str(r) == str(self.bot.create_emoji_str('s_move_right',653161518170505216)): 46 | if page == pmax: 47 | page = 0 48 | else: 49 | page = page + 1 50 | elif str(r) == str(self.bot.create_emoji_str("s_move_left",653161518195671041)): 51 | if page == 0: 52 | page = pmax 53 | else: 54 | page = page - 1 55 | await msg.edit(embed=await get_page(page)) 56 | 57 | 58 | 59 | async def setup(bot): 60 | await bot.add_cog(m10s_partner(bot)) 61 | -------------------------------------------------------------------------------- /apple_util.py: -------------------------------------------------------------------------------- 1 | import time 2 | import datetime 3 | import discord 4 | 5 | # AppleUtil - utility tool made by apple502j. 6 | 7 | 8 | class RateLimiter(object): 9 | """rate: int, per: seconds""" 10 | 11 | def __init__(self, rate, per): 12 | self.rate = rate 13 | self.per = per 14 | self._token_left = rate 15 | self._last_updated = time.time() 16 | 17 | def use(self): 18 | _upd = time.time() 19 | if self._last_updated + self.per < _upd: 20 | self._last_updated = _upd 21 | self._token_left = self.rate - 1 22 | return True 23 | if self._token_left > 0: 24 | self._token_left -= 1 25 | return True 26 | return False 27 | 28 | 29 | class AppleUtil(object): 30 | def __init__(self, bot): 31 | self.bot = bot 32 | 33 | @staticmethod 34 | def has_all_perms(member, channel, *perms): 35 | """Check if someone has all permissions. channel may be None for global permissions.""" 36 | if not isinstance(member, discord.Member): 37 | raise TypeError( 38 | "has_all_perms take a Member as its first argument") 39 | if channel: 40 | perm = channel.permissions_for(member) 41 | else: 42 | perm = member.guild_permissions 43 | for p in perms: 44 | if not getattr(perm, p, False): 45 | return False 46 | return True 47 | 48 | @staticmethod 49 | def create_throttle(rate, per): 50 | return RateLimiter(rate, per) 51 | 52 | async def get_as_binary(self, url): 53 | async with self.bot.session.get(url) as resp: 54 | resp.raise_for_status() 55 | return await resp.read() 56 | 57 | async def get_as_json(self, url): 58 | async with self.bot.session.get(url) as resp: 59 | resp.raise_for_status() 60 | return await resp.json() 61 | 62 | async def get_as_text(self, url): 63 | async with self.bot.session.get(url) as resp: 64 | resp.raise_for_status() 65 | return await resp.text() 66 | 67 | @staticmethod 68 | def within(dt, minutes): 69 | return (datetime.datetime.now(datetime.timezone.utc) - dt) < datetime.timedelta(minutes=minutes) 70 | 71 | 72 | def setup(bot): 73 | pass 74 | -------------------------------------------------------------------------------- /cogs/hybrid/m10s_quick_cmd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Union 4 | import discord 5 | from discord.ext import commands 6 | import asyncio 7 | import datetime 8 | 9 | from discord import app_commands 10 | 11 | import m10s_util as ut 12 | 13 | from my_module import dpy_interaction as dpyui 14 | 15 | 16 | 17 | class m10s_quick_cmd(commands.Cog): 18 | 19 | def __init__(self, bot): 20 | self.bot = bot 21 | 22 | @commands.hybrid_command(name="shortcut", description="クイックコマンド呼び出しのパネルを作成します。") 23 | @app_commands.describe(label="呼び出しボタンの表示名") 24 | @app_commands.describe(run_command="呼び出すコマンド") 25 | @ut.runnable_check() 26 | @ut.runnable_check_for_appcmd() 27 | async def quick_command_create(self, ctx:commands.Context, label:str, *, run_command:str): 28 | if run_command == "": 29 | await ctx.send("> 実行コマンドの指定がありません!") 30 | return 31 | act_button = dpyui.interaction_buttons() 32 | act_button.add_button( 33 | label = label, 34 | style = dpyui.button_style.Primary, 35 | custom_id = f"quickact:{'__'.join(run_command.split())}" 36 | ) 37 | embed = discord.Embed(title="クイックコマンド呼び出し", description=f"このメッセージのボタンを押すと`{run_command}`コマンドが実行されます。", color=self.bot.ec) 38 | embed.set_footer(text="使用しなくなった場合は、このメッセージを削除してください。") 39 | await self.bot.dpyui.send_with_ui(ctx.channel, "", embed=embed, ui = act_button) 40 | if ctx.interaction: 41 | await ctx.interaction.response.send_message("> 設定しました。", ephemeral=True) 42 | 43 | @commands.Cog.listener() 44 | async def on_button_click(self, cb:dpyui.interaction_button_callback): 45 | if cb.custom_id.startswith("quickact:"): 46 | act_cmd = " ".join(cb.custom_id.replace("quickact:", "").split("__")) 47 | 48 | _msg = cb.message 49 | _msg.id = int(cb.interaction_id) 50 | _msg.channel = cb._channel 51 | _msg.author = cb.user 52 | _msg.content = f"s-{act_cmd}" 53 | 54 | _ctx = await self.bot.get_context(_msg) 55 | await cb.send_response_with_ui(content=f"> クイックコマンド呼び出し(ベータ版)による{cb.user}のコマンド`{act_cmd}`の実行") 56 | await self.bot.invoke(_ctx) 57 | 58 | 59 | async def setup(bot): 60 | await bot.add_cog(m10s_quick_cmd(bot)) 61 | -------------------------------------------------------------------------------- /cogs/apple_foc.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import discord 4 | from discord.ext import commands 5 | 6 | 7 | class AppleFOCCog(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.db = bot.cursor 11 | self.guild_throttle = {} 12 | 13 | async def get_log_channel(self, guild_id): 14 | guild = await self.db.fetchone( 15 | "SELECT * FROM guilds WHERE id=%s", (guild_id,)) 16 | #guild = await self.bot.cursor.fetchone() 17 | if not guild: 18 | return None 19 | if guild["sendlog"]: 20 | return self.bot.get_channel(guild["sendlog"]) 21 | return None 22 | 23 | def should_check(self, member): 24 | member_id = f"{member.guild.id}-{member.id}" 25 | if member_id in self.guild_throttle: 26 | return self.guild_throttle[member_id].use() 27 | else: 28 | self.guild_throttle[member_id] = self.bot.apple_util.create_throttle( 29 | 1, 60) 30 | self.guild_throttle[member_id].use() 31 | return True 32 | 33 | async def is_offline(self, member): 34 | return member.status is discord.Status.offline and await self.bot.can_use_online(member) 35 | 36 | async def send(self, member, logc): 37 | e = discord.Embed(title="オンライン隠し", description=member.mention, 38 | timestamp=datetime.datetime.now(datetime.timezone.utc)) 39 | return 40 | 41 | async def run(self, member): 42 | if member.bot or not await self.is_offline(member): 43 | return 44 | logc = await self.get_log_channel(member.guild.id) 45 | if not logc: 46 | return 47 | if self.should_check(member): 48 | await self.send(member, logc) 49 | 50 | @commands.Cog.listener() 51 | async def on_typing(self, ch, member, when): 52 | if isinstance(ch, discord.DMChannel) or isinstance(member, discord.User): 53 | return 54 | await self.run(member) 55 | 56 | @commands.Cog.listener() 57 | async def on_message(self, msg): 58 | if not msg.guild: 59 | return 60 | await self.run(msg.author) 61 | 62 | @commands.Cog.listener() 63 | async def on_reaction_add(self, r, m): 64 | if isinstance(m, discord.Member): 65 | await self.run(m) 66 | 67 | 68 | async def setup(bot): 69 | await bot.add_cog(AppleFOCCog(bot)) 70 | -------------------------------------------------------------------------------- /checker.py: -------------------------------------------------------------------------------- 1 | import re 2 | import statistics 3 | 4 | INVITE_REGEX = re.compile(r'(?:https?\:\/\/)?discord(?:\.gg|(?:app)?\.com\/invite)\/([a-zA-Z0-9]+)') 5 | CUSTOM_EMOJI_REGEX = re.compile(r'') 6 | UNICODE_EMOJI_REGEX = re.compile(r'[\U00002600-\U000027BF]|[\U0001f300-\U0001f64F]|[\U0001f680-\U0001f6FF]') 7 | 8 | class MaliciousInput(Exception): 9 | """Input was malicious or spammy.""" 10 | def __init__(self, reason='Unknown reason', should_ban=False): 11 | super().__init__() 12 | self.reason = reason 13 | self.should_ban = should_ban 14 | 15 | def content_checker(bot, message): 16 | """Checks input, and raises MaliciousInput when it is""" 17 | content = message.content 18 | is_team = message.author.id in bot.team_sina 19 | 20 | # mentions 21 | if message.mention_everyone: 22 | raise MaliciousInput('mentions at-everyone/at-here', True) 23 | 24 | if '@everyone' in content or '@here' in content: 25 | raise MaliciousInput('mentions at-everyone/at-here', True) 26 | 27 | if len(message.mentions) > 5: 28 | raise MaliciousInput('too many mentions') 29 | 30 | if len(message.raw_mentions) > 5: 31 | raise MaliciousInput('too many mentions') 32 | 33 | if len(message.role_mentions) > 3: 34 | raise MaliciousInput('too many role mentions') 35 | 36 | if len(message.raw_role_mentions) > 3: 37 | raise MaliciousInput('too many role mentions') 38 | 39 | if not is_team: 40 | invites = len(INVITE_REGEX.findall(message.content)) 41 | if invites > 2: 42 | raise MaliciousInput('invite link (insta-ban)', True) 43 | elif invites: 44 | raise MaliciousInput('invite link') 45 | 46 | lines = message.content.split('\n') 47 | if len(lines) > 10: 48 | line_lengths = [len(line) for line in lines] 49 | if statistics.median(line_lengths) < 4: 50 | raise MaliciousInput('too many short lines') 51 | 52 | custom_emojis = len(CUSTOM_EMOJI_REGEX.findall(message.content)) 53 | if custom_emojis > 25: 54 | raise MaliciousInput('custom emoji spam (insta-ban)', True) 55 | elif custom_emojis > 10: 56 | raise MaliciousInput('custom emoji spam') 57 | 58 | unicode_emojis = len(UNICODE_EMOJI_REGEX.findall(message.content)) 59 | if unicode_emojis > 40: 60 | raise MaliciousInput('unicode emoji spam (insta-ban)', True) 61 | elif unicode_emojis > 20: 62 | raise MaliciousInput('unicode emoji spam') 63 | -------------------------------------------------------------------------------- /cogs/m10s_app_metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands, tasks 5 | import asyncio 6 | from dateutil.relativedelta import relativedelta as rdelta 7 | import traceback 8 | import m10s_util as ut 9 | import textwrap 10 | 11 | import config 12 | import aiohttp 13 | 14 | from discord import app_commands 15 | 16 | class m10s_app_metadata(commands.Cog): 17 | 18 | def __init__(self, bot): 19 | self.bot = bot 20 | 21 | @commands.command() 22 | @commands.is_owner() 23 | @ut.runnable_check() 24 | async def update_role_metadata(self, ctx): 25 | json_body = [ 26 | { 27 | "key":"isadmin", 28 | "name":"チーム☆思惟奈ちゃん", 29 | "description":"思惟奈ちゃん運営である", 30 | "type":7 31 | }, 32 | { 33 | "key":"isdonate", 34 | "name":"思惟奈ちゃんプレミアム会員", 35 | "description":"思惟奈ちゃんプレミアム会員のランク", 36 | "type":2 37 | }, 38 | { 39 | "key":"isverified", 40 | "name":"思惟奈ちゃん認証済みアカウント", 41 | "description":"思惟奈ちゃん上で、アカウントが認証されている", 42 | "type":7 43 | }, 44 | { 45 | "key":"gamepoint", 46 | "name":"ゲームポイント", 47 | "description":"一部ゲームでたまる思惟奈ちゃんゲームポイントを、指定値以上所有している", 48 | "type":2 49 | } 50 | ] 51 | headers = { 52 | "Authorization": f"Bot {config.BOT_TOKEN}" 53 | } 54 | async with aiohttp.ClientSession() as session: 55 | async with session.put(url=f"https://discord.com/api/v10/applications/{config.DISCORD_CLIENT_ID}/role-connections/metadata", json=json_body, headers=headers) as resp: 56 | resp.raise_for_status() 57 | await ctx.send("> 設定しました。") 58 | 59 | @commands.hybrid_command(description="あなたの接続されているアプリメタデータを更新します。") 60 | @ut.runnable_check() 61 | @ut.runnable_check_for_appcmd() 62 | async def sync_metadata(self, ctx:commands.Context): 63 | await ctx.defer(ephemeral=True) 64 | upf = await self.bot.cursor.fetchone("select oauth_ref_token from users where id=%s", (ctx.author.id,)) 65 | if upf["oauth_ref_token"]: 66 | try: 67 | await self.bot.update_connect_metadata(ctx.author.id, upf["oauth_ref_token"], True) 68 | except: 69 | await ctx.send("失敗しました。\n一度連携を外し、再度連携させてください。", ephemeral=True) 70 | else: 71 | await ctx.send("完了しました。", ephemeral=True) 72 | else: 73 | await ctx.send("失敗しました。\nまだアプリ連携が行われていません。Linked Rolesメニューから、思惟奈ちゃんの連携が有効なロールを探して連携してください。", ephemeral=True) 74 | 75 | 76 | async def setup(bot): 77 | await bot.add_cog(m10s_app_metadata(bot)) -------------------------------------------------------------------------------- /cogs/pf9_symmetry.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | from PIL import Image, ImageOps 7 | 8 | from discord import app_commands 9 | 10 | import m10s_util as ut 11 | 12 | class Symmetry(commands.Cog): 13 | 14 | def __init__(self, bot): 15 | self.bot = bot 16 | 17 | @commands.hybrid_command(name="symmetry", description="シンメトリー画像を生成します。") 18 | @commands.bot_has_permissions(attach_files=True) 19 | @app_commands.checks.bot_has_permissions(attach_files=True) 20 | @discord.app_commands.choices(side=[ 21 | discord.app_commands.Choice(name="left", value=0), 22 | discord.app_commands.Choice(name="right", value=1), 23 | discord.app_commands.Choice(name="up", value=2), 24 | discord.app_commands.Choice(name="down", value=3), 25 | ]) 26 | @app_commands.describe(side="どの面をシンメトリーにするか") 27 | @app_commands.describe(image="シンメトリー加工する画像") 28 | @ut.runnable_check() 29 | @ut.runnable_check_for_appcmd() 30 | async def symmetry(self, ctx: commands.Context, side: int, image: discord.Attachment): 31 | if ctx.interaction: 32 | await ctx.defer() 33 | async with ctx.channel.typing(): 34 | await image.save("image.png") 35 | img = Image.open('image.png') 36 | if side == 0: 37 | tmp1 = img.crop((0, 0, img.size[0] // 2, img.size[1])) 38 | elif side == 1: 39 | tmp1 = img.crop( 40 | (img.size[0] // 2, 0, img.size[0], img.size[1])) 41 | elif side == 2: 42 | tmp1 = img.crop((0, 0, img.size[0], img.size[1] // 2)) 43 | elif side == 3: 44 | tmp1 = img.crop( 45 | (0, img.size[0] // 2, img.size[0], img.size[1])) 46 | else: 47 | raise ValueError(f"Invalid value for 'side': {side}. Expected 0, 1, 2, or 3.") 48 | if side == 0 or side == 1: 49 | tmp2 = ImageOps.mirror(tmp1) 50 | dst = Image.new( 51 | 'RGB', (tmp1.width + tmp2.width, tmp1.height)) 52 | else: 53 | tmp2 = ImageOps.flip(tmp1) 54 | dst = Image.new( 55 | 'RGB', (tmp1.width, tmp1.height + tmp2.height)) 56 | if side == 0: 57 | dst.paste(tmp1, (0, 0)) 58 | dst.paste(tmp2, (tmp1.width, 0)) 59 | if side == 1: 60 | dst.paste(tmp1, (tmp2.width, 0)) 61 | dst.paste(tmp2, (0, 0)) 62 | if side == 2: 63 | dst.paste(tmp1, (0, 0)) 64 | dst.paste(tmp2, (0, tmp1.height)) 65 | if side == 3: 66 | dst.paste(tmp1, (0, tmp2.height)) 67 | dst.paste(tmp2, (0, 0)) 68 | dst.save('ts2.png') 69 | await ctx.send(file=discord.File('ts2.png')) 70 | 71 | 72 | async def setup(bot): 73 | await bot.add_cog(Symmetry(bot)) 74 | -------------------------------------------------------------------------------- /cogs/syouma.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | 7 | import m10s_util as ut 8 | from bs4 import BeautifulSoup 9 | import random 10 | 11 | from discord import app_commands 12 | 13 | """↑様々な便利コマンド詰め合わせ 14 | ut.textto("キー",Member) 15 | ユーザーの言語設定に基づいてキーのテキストを返す。 16 | ut.ondevicon(Member) 17 | オンライン状況に基づくデバイスアイコンテキストを返す。 18 | ut.getEmbed(title,description,color,(name,value)...) 19 | Embedのお手軽生成。これ使ったのがあるから消そうにも消せない。 20 | await ut.opendm(Member/User) 21 | DMチャンネルを返します。DMチャンネルが存在しないなんてことでは困らせません。 22 | await wait_message_return(ctx,質問するテキスト,←の送信先,待つ時間): 23 | 入力待ちの簡略化。タイムアウトの例外キャッチを忘れずに 24 | """ 25 | 26 | 27 | class syouma(commands.Cog): 28 | 29 | def __init__(self, bot): 30 | self.bot = bot 31 | 32 | @commands.hybrid_command(name="tenki",description="天気情報を表示します。") 33 | @app_commands.describe(address="表示する市などの名称") 34 | @ut.runnable_check() 35 | @ut.runnable_check_for_appcmd() 36 | async def tenki(self, ctx, address:str): 37 | color = random.randint(0x000000, 0xffffff) 38 | Url = "https://tenki.jp" 39 | Req = await self.bot.apple_util.get_as_text(Url + "/search/?keyword=" + address) 40 | Soup = BeautifulSoup(Req, 'lxml') 41 | Sed = Soup.find_all(class_="search-entry-data") 42 | HrfUrl = None 43 | for val in Sed: 44 | if val.find(class_="address").text.find("以下に掲載がない場合"): 45 | HrfUrl = val.a.get("href") 46 | # print(HrfUrl) 47 | # myDict = {} 48 | # 住所からhrefを取得 49 | if HrfUrl is None: 50 | return await ctx.send("> 天気確認\n その地域では見つかりませんでした。") 51 | await asyncio.sleep(1) # 一回requestを投げているので1秒待つ 52 | Req = await self.bot.apple_util.get_as_text(Url + HrfUrl) 53 | # print(Req) 54 | bsObj = BeautifulSoup(Req, "html.parser") 55 | today = bsObj.find(class_="today-weather") 56 | weather = today.p.string 57 | temp = today.div.find(class_="date-value-wrap") 58 | temp = temp.find_all("dd") 59 | temp_max = temp[0].span.string # 最高気温 60 | temp_max_diff = temp[1].string # 最高気温の前日比 61 | temp_min = temp[2].span.string # 最低気温 62 | temp_min_diff = temp[3].string # 最低気温の前日比 63 | todayni = bsObj.find(class_="tomorrow-weather") 64 | weatherni = todayni.p.string 65 | tempni = todayni.div.find(class_="date-value-wrap") 66 | tempni = tempni.find_all("dd") 67 | temp_maxni = tempni[0].span.string # 最高気温 68 | temp_max_diffni = tempni[1].string # 最高気温の前日比 69 | temp_minni = tempni[2].span.string # 最低気温 70 | temp_min_diffni = tempni[3].string # 最低気温の前日比 71 | await ctx.send(embed=discord.Embed(title=f"{address}の今日の天気:{weather}\n明日の天気:{weatherni}", description=f"今日の最高気温:{temp_max} {temp_max_diff}\n今日の最低気温:{temp_min} {temp_min_diff}\n明日の最高気温:{temp_maxni} {temp_max_diffni}\n明日の最低気温:{temp_minni} {temp_min_diffni}", color=color)) 72 | 73 | async def setup(bot): 74 | await bot.add_cog(syouma(bot)) 75 | -------------------------------------------------------------------------------- /cogs/P143_jyanken.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from __future__ import annotations 4 | 5 | import discord 6 | from discord.ext import commands 7 | from typing import Any 8 | import random 9 | import asyncio 10 | 11 | import m10s_util as ut 12 | 13 | from discord import app_commands 14 | 15 | # このプログラムはPoteto143氏によって作成され、yaakiyuによって改善されています。 16 | 17 | 18 | class ContentEvent(asyncio.Event): 19 | content: Any 20 | 21 | 22 | class JankenView(discord.ui.View): 23 | msg: discord.Message 24 | 25 | def __init__(self, event: ContentEvent): 26 | super().__init__(timeout=15.0) 27 | self.event = event 28 | self.closed = False 29 | 30 | async def on_timeout(self): 31 | if self.closed: 32 | return 33 | await self.msg.edit(view=None, embed=discord.Embed( 34 | title="ジャンケン", description="15秒間操作がされなかったのでゲームを終了しました。", 35 | color=0xff0000 36 | )) 37 | self.event.set() 38 | 39 | @discord.ui.button(emoji="🖐") 40 | async def pa(self, interaction: discord.Interaction, _): 41 | self.event.content = (interaction, 0) 42 | self.event.set() 43 | 44 | @discord.ui.button(emoji="✊") 45 | async def gu(self, interaction: discord.Interaction, _): 46 | self.event.content = (interaction, 1) 47 | self.event.set() 48 | 49 | @discord.ui.button(emoji="✌️") 50 | async def tyoki(self, interaction: discord.Interaction, _): 51 | self.event.content = (interaction, 2) 52 | self.event.set() 53 | 54 | 55 | class jyanken(commands.Cog): 56 | 57 | def __init__(self, bot): 58 | self.bot = bot 59 | 60 | EMOJIS = ["🖐", "✊", "✌️"] 61 | 62 | RESULT_PATTERNS = [ 63 | ["あいこで...", "あいこで...", "まだまだ!あいこで..."], 64 | ["残念、負けちゃった...", "あーあ、負けちゃった。", "負けた、くやしい!"], 65 | ["やった、私の勝ち!", "私の勝ち、残念でした!", "勝った!また挑戦してね!"] 66 | ] 67 | 68 | @commands.hybrid_command(name="jyanken", description="じゃんけんできます。") 69 | @commands.bot_has_permissions(manage_messages=True) 70 | @app_commands.checks.bot_has_permissions(manage_messages=True) 71 | @ut.runnable_check() 72 | @ut.runnable_check_for_appcmd() 73 | async def command(self, ctx): 74 | embed = discord.Embed( 75 | title="ジャンケン", description="ジャンケンをするよ。\n最初はグー、ジャンケン…", 76 | color=0xffff00 77 | ) 78 | event = ContentEvent() 79 | view = JankenView(event) 80 | msg = await ctx.send(embed=embed, view=view) 81 | view.msg = msg 82 | 83 | while True: 84 | await event.wait() 85 | 86 | hands = [event.content[1], random.randint(0, 2)] # [ユーザー, Bot] 87 | if hands[0] == hands[1]: 88 | await event.content[0].response.edit_message(embed=discord.Embed( 89 | title="ジャンケン", description=f"ポン!{self.EMOJIS[hands[1]]}\n{random.choice(self.RESULT_PATTERNS[0])}", 90 | color=self.bot.ec 91 | )) 92 | event.clear() 93 | continue 94 | 95 | if (hands[0] + 1) % 3 == hands[1]: 96 | result = random.choice(self.RESULT_PATTERNS[1]) # ユーザーの勝ち 97 | else: 98 | result = random.choice(self.RESULT_PATTERNS[2]) # ユーザーの負け 99 | 100 | await event.content[0].response.edit_message(embed=discord.Embed( 101 | title="ジャンケン", description=f"ポン!{self.EMOJIS[hands[1]]}\n{result}", 102 | color=self.bot.ec 103 | ), view=None) 104 | view.closed = True 105 | return 106 | 107 | 108 | async def setup(bot): 109 | await bot.add_cog(jyanken(bot)) 110 | -------------------------------------------------------------------------------- /cogs/apple_misc.py: -------------------------------------------------------------------------------- 1 | import json 2 | import datetime 3 | from typing import Optional 4 | import discord 5 | from discord.ext import commands, tasks 6 | from discord import app_commands 7 | 8 | import m10s_util as ut 9 | 10 | import time 11 | 12 | LANGUAGE = {"python", "javascript"} 13 | BLOCKS = {"if", "while", "for", "def"} 14 | BLOCK_END = "block_end" 15 | 16 | CODE = [ 17 | ("ask", "variable", "string..."), 18 | ("for", "char", "variable"), 19 | ("print", "char"), 20 | ("block_end",) 21 | ] 22 | 23 | PING_CH = 712564878480637973 24 | 25 | 26 | class AppleMiscCog(commands.Cog): 27 | def __init__(self, bot): 28 | self.bot = bot 29 | self.report_ping.start() 30 | 31 | @commands.command() 32 | @ut.runnable_check() 33 | async def code_in(self, ctx, lang): 34 | if lang in LANGUAGE: 35 | with open(f"data/programming_info/{lang}.json", "r", encoding="utf-8") as f: 36 | lang_def = json.load(f) 37 | result = "" 38 | indent_times = 0 39 | for line in CODE: 40 | suffix = lang_def["after_line"] 41 | addition = "" 42 | if line[0] == BLOCK_END: 43 | indent_times -= 1 44 | suffix = lang_def["after_block"] 45 | elif line[0] in BLOCKS: 46 | suffix = lang_def["before_block"] 47 | addition = lang_def[line[0]].format(*line[1:]) 48 | else: 49 | addition = lang_def[line[0]].format(*line[1:]) 50 | indents = lang_def["indent"] * indent_times 51 | result += f"{indents}{addition}{suffix}" 52 | if line[0] in BLOCKS: 53 | indent_times += 1 54 | await ctx.send(f"```{result}```") 55 | 56 | @commands.command() 57 | @commands.is_owner() 58 | @ut.runnable_check() 59 | async def clear_l10n_cache(self, ctx): 60 | self.bot.translate_handler.clean_cache() 61 | 62 | @commands.hybrid_command(description="Botの応答速度を返します。") 63 | @ut.runnable_check() 64 | @ut.runnable_check_for_appcmd() 65 | @app_commands.describe(is_debug="詳細情報を表示するかどうか(デバッグ用)") 66 | @app_commands.allowed_contexts(guilds=True, dms=True, private_channels=True) 67 | @app_commands.allowed_installs(guilds=True, users=True) 68 | async def ping(self, ctx, is_debug: Optional[bool] = False): 69 | if is_debug: 70 | message_time = ctx.message.created_at.timestamp() 71 | time_before_send = datetime.datetime.now(datetime.timezone.utc).timestamp() 72 | msg = await ctx.send("...") 73 | time_after_send = datetime.datetime.now(datetime.timezone.utc).timestamp() 74 | latency = self.bot.latency 75 | tb = abs(time_before_send - message_time) 76 | ba = abs(time_after_send - time_before_send) 77 | content = f"LA: {latency:.3}\nTB: {tb:.3}\nBA: {ba:.3}\n" 78 | if hasattr(ctx, "context_at") and isinstance(ctx.context_at, float): 79 | context_at = ctx.context_at 80 | tc = abs(context_at - message_time) 81 | cb = abs(time_before_send - context_at) 82 | content += f"TC: {tc:.3}\nCB: {cb:.3}\n" 83 | await msg.edit(content=content) 84 | else: 85 | startt = time.time() 86 | mes = await ctx.send("please wait") 87 | await mes.edit(content=str(round(time.time()-startt, 3)*1000)+"ms") 88 | 89 | @tasks.loop(minutes=30) 90 | async def report_ping(self): 91 | channel = await self.bot.fetch_channel(PING_CH) 92 | time_before_send = datetime.datetime.now(datetime.timezone.utc).timestamp() 93 | msg = await channel.send("...") 94 | time_after_send = datetime.datetime.now(datetime.timezone.utc).timestamp() 95 | ba = abs(time_after_send - time_before_send) 96 | await msg.edit(content=f"LA: {self.bot.latency:.3}\nBA: {ba:.3}") 97 | 98 | @report_ping.before_loop 99 | async def before_report_loop(self): 100 | await self.bot.wait_until_ready() 101 | 102 | 103 | async def setup(bot): 104 | await bot.add_cog(AppleMiscCog(bot)) 105 | -------------------------------------------------------------------------------- /paginator.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | class CannotPaginate(Exception): 4 | pass 5 | 6 | class EmbedPaginator: 7 | 8 | def __init__(self, ctx, *, entries, timeout=30.0): 9 | self.ctx = ctx 10 | self.bot = ctx.bot 11 | self.entries = entries 12 | self.max_page = len(entries) - 1 13 | self.page = 0 14 | self.timeout = timeout 15 | 16 | if ctx.guild is None: 17 | perm = self.ctx.channel.permissions_for(ctx.bot.user) 18 | else: 19 | perm = self.ctx.channel.permissions_for(ctx.guild.me) 20 | 21 | if not perm.embed_links: 22 | raise CannotPaginate("Missing Permissions : embed_links") 23 | if not perm.send_messages: 24 | raise CannotPaginate("Missing Permissions : send_messages") 25 | if not perm.add_reactions: 26 | raise CannotPaginate("Missing Permissions : add_reactions") 27 | if not perm.read_message_history: 28 | raise CannotPaginate("Missing Permissions : read_message_history") 29 | 30 | def get_page(self, page:int): 31 | self.page = page 32 | return self.entries[page] 33 | 34 | async def paginate(self): 35 | msg = await self.ctx.send(embed=self.entries[self.page]) 36 | 37 | tasks = [] 38 | tasks.append(msg.add_reaction('⏮')) 39 | tasks.append(msg.add_reaction('◀')) 40 | tasks.append(msg.add_reaction('⏹️')) 41 | tasks.append(msg.add_reaction('▶')) 42 | tasks.append(msg.add_reaction('⏭️')) 43 | tasks.append(msg.add_reaction('🔢')) 44 | 45 | asyncio.gather(*tasks) 46 | 47 | def check(reaction, user): 48 | if reaction.message.id == msg.id and user.id == self.ctx.message.author.id: 49 | return True 50 | else: 51 | return False 52 | 53 | while True: 54 | try: 55 | reaction, user = await self.bot.wait_for("reaction_add", check=check, timeout=self.timeout) 56 | except: 57 | break 58 | 59 | try: 60 | await msg.remove_reaction(reaction, user) 61 | except: 62 | pass 63 | 64 | if str(reaction) == "⏮": 65 | embed = self.get_page(0) 66 | elif str(reaction) == "◀": 67 | if self.page == 0: 68 | embed = self.get_page(self.max_page) 69 | else: 70 | embed = self.get_page(self.page - 1) 71 | elif str(reaction) == "⏹️": 72 | cl = [] 73 | cl.append(msg.remove_reaction('⏮', self.bot.user)) 74 | cl.append(msg.remove_reaction('◀', self.bot.user)) 75 | cl.append(msg.remove_reaction('⏹️', self.bot.user)) 76 | cl.append(msg.remove_reaction('▶', self.bot.user)) 77 | cl.append(msg.remove_reaction('⏭️', self.bot.user)) 78 | cl.append(msg.remove_reaction('🔢', self.bot.user)) 79 | 80 | try: 81 | await asyncio.gather(*cl) 82 | except: 83 | pass 84 | 85 | break 86 | elif str(reaction) == "▶": 87 | if self.page == self.max_page: 88 | embed = self.get_page(0) 89 | else: 90 | embed = self.get_page(self.page + 1) 91 | elif str(reaction) == "⏭️": 92 | embed = self.get_page(self.max_page) 93 | elif str(reaction) == "🔢": 94 | a = await self.ctx.send("> 何ページに移動しますか?") 95 | 96 | try: 97 | m = await self.bot.wait_for("message", check=lambda message: message.channel.id == self.ctx.channel.id and message.author.id == self.ctx.message.author.id and str(message.content).isdigit(), timeout=self.timeout) 98 | except: 99 | embed = self.get_page(0) 100 | else: 101 | try: 102 | await a.delete() 103 | except: 104 | pass 105 | 106 | p = int(m.content) - 1 107 | 108 | if p > self.max_page: 109 | embed = self.get_page(self.max_page) 110 | elif p < 0: 111 | embed = self.get_page(0) 112 | else: 113 | embed = self.get_page(p) 114 | 115 | await msg.edit(embed=embed) -------------------------------------------------------------------------------- /cogs/apple_onlinenotif.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import discord 3 | from discord.ext import commands 4 | from discord import app_commands 5 | 6 | import json 7 | 8 | import m10s_util as ut 9 | 10 | # This file is copied from TeraAppleBot 11 | 12 | 13 | class OnlineNotif(commands.Cog): 14 | def __init__(self, bot): 15 | self.bot = bot 16 | 17 | def get_member(user_id): 18 | m = [g.get_member(user_id) 19 | for g in bot.guilds if g.get_member(user_id)] 20 | if m: 21 | return m[0] 22 | return None 23 | self.bot.get_member = get_member 24 | self.onlinenotif_enabled = bot.can_use_online 25 | 26 | self._last_posted = {} 27 | 28 | async def get_subscribers(self): 29 | p = await self.bot.cursor.fetchall("SELECT id, onnotif FROM users") 30 | return [ 31 | {"user_id": int(i["id"]), "subscribe": [int(j) for j in (json.loads(i["onnotif"]) or []) if j]} 32 | for i 33 | in p 34 | ] 35 | 36 | async def get_subscribing_of_user(self, user): 37 | p = await self.bot.cursor.fetchone("SELECT onnotif FROM users WHERE id = %s", (user.id,)) 38 | return json.loads(p["onnotif"]) or [] 39 | 40 | async def get_subscribed_of_user(self, user): 41 | return [ 42 | i["user_id"] 43 | for i 44 | in await self.get_subscribers() 45 | if user.id in i["subscribe"] 46 | ] 47 | 48 | # @commands.Cog.listener() 49 | async def on_presence_update(self, before, after): 50 | if before.status == after.status: 51 | return 52 | if discord.Status.offline not in (before.status, after.status): 53 | return 54 | if not await self.onlinenotif_enabled(before): 55 | return 56 | if self._last_posted.get(before.id, None) and self.bot.apple_util.within(self._last_posted[before.id], 3): 57 | return 58 | self._last_posted[before.id] = discord.utils.utcnow() 59 | msg = "onlinenotif-notif" if before.status is discord.Status.offline else "onlinenotif-offlinenotif" 60 | for subsc in await self.get_subscribed_of_user(before): 61 | if self.bot.shares_guild(before.id, subsc) and self.bot.get_member(subsc).status != discord.Status.offline: 62 | user = self.bot.get_user(subsc) 63 | await user.send(await self.bot._(user, msg, str(before))) 64 | 65 | @commands.hybrid_group(invoke_without_command=True) 66 | @ut.runnable_check() 67 | @ut.runnable_check_for_appcmd() 68 | async def onlinenotif(self, ctx): 69 | """Returns the name of the users you are receiving online notifications of.""" 70 | users = [ 71 | self.bot.get_user(user_id) 72 | for user_id 73 | in await self.get_subscribing_of_user(ctx.author) 74 | if self.bot.shares_guild(ctx.author.id, user_id) 75 | ] 76 | if not users: 77 | return await ctx.author.send(await ctx._("onlinenotif-nousers")) 78 | await ctx.author.send(await ctx._("onlinenotif-subscribing", " ".join(map(str, users)))) 79 | 80 | @onlinenotif.command(aliases=["add"]) 81 | @ut.runnable_check() 82 | @ut.runnable_check_for_appcmd() 83 | async def subscribe(self, ctx, user: discord.User): 84 | """Subscribes to this user.""" 85 | subscribing = await self.get_subscribing_of_user(ctx.author) 86 | if user.id in subscribing: 87 | return await ctx.say("onlinenotif-already") 88 | if not self.bot.shares_guild(ctx.author.id, user.id): 89 | return await ctx.say("onlinenotif-shareGuild") 90 | subscribing.append(user.id) 91 | await self.bot.cursor.execute( 92 | "UPDATE users SET onnotif = %s WHERE id = %s", (json.dumps(subscribing), ctx.author.id)) 93 | await ctx.say("onlinenotif-success") 94 | 95 | @onlinenotif.command(aliases=["remove", "del"]) 96 | @ut.runnable_check() 97 | @ut.runnable_check_for_appcmd() 98 | async def unsubscribe(self, ctx, user: discord.User): 99 | """Un-subscribes to this user.""" 100 | subscribing = await self.get_subscribing_of_user(ctx.author) 101 | if user.id not in subscribing: 102 | return await ctx.say("onlinenotif-yet") 103 | subscribing.remove(user.id) 104 | await self.bot.cursor.execute( 105 | "UPDATE users SET onnotif = %s WHERE id = %s", (json.dumps(subscribing), ctx.author.id)) 106 | await ctx.say("onlinenotif-removeSuccess") 107 | 108 | @onlinenotif.command() 109 | @ut.runnable_check() 110 | @ut.runnable_check_for_appcmd() 111 | async def settings(self, ctx, enabled: bool=False): 112 | """Choose whether someone can receive your online notification. 113 | Note that you have to share server with that user.""" 114 | await self.bot.cursor.execute( 115 | "UPDATE users SET online_agreed = %s WHERE id = %s", (int(enabled), ctx.author.id)) 116 | await ctx.say("onlinenotif-settings") 117 | 118 | 119 | async def setup(bot): 120 | await bot.add_cog(OnlineNotif(bot)) 121 | -------------------------------------------------------------------------------- /cogs/slash/m10s_messageinfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | from discord import app_commands 6 | import asyncio 7 | from dateutil.relativedelta import relativedelta as rdelta 8 | from typing import Union 9 | import traceback 10 | 11 | import m10s_util as ut 12 | """↑様々な便利コマンド詰め合わせ 13 | ut.ondevicon(Member) 14 | オンライン状況に基づくデバイスアイコンテキストを返す。 15 | ut.getEmbed(title,description,color,(name,value)...) 16 | Embedのお手軽生成。これ使ったのがあるから消そうにも消せない。 17 | await ut.opendm(Member/User) 18 | DMチャンネルを返します。DMチャンネルが存在しないなんてことでは困らせません。 19 | await wait_message_return(ctx,質問するテキスト,←の送信先,待つ時間): 20 | 入力待ちの簡略化。タイムアウトの例外キャッチを忘れずに 21 | ut.get_vmusic(bot,member) 22 | 思惟奈ちゃんの音楽再生機能でそのメンバーがきいている曲を返します。 23 | """ 24 | 25 | 26 | class m10s_messageinfo(commands.Cog): 27 | 28 | def __init__(self, bot): 29 | self.bot = bot 30 | 31 | @app_commands.context_menu(name="message info") 32 | @ut.runnable_check() 33 | async def message_info(interaction:discord.Interaction, msg:discord.Message): 34 | #msgに入ったメッセージで詳細情報Embedを作成して送信。 35 | if msg.is_system(): 36 | author_status = "[System]" 37 | elif msg.webhook_id: 38 | author_status = "[WebHook]" 39 | elif msg.author.bot: 40 | if msg.author.public_flags.verified_bot: 41 | author_status = "[✅Bot]" 42 | else: 43 | author_status = "[Bot]" 44 | else: 45 | author_status = "" 46 | 47 | e = discord.Embed(title=f"メッセージ情報", description=msg.system_content, color=self.bot.ec) 48 | e.set_author(name=f"{msg.author.display_name}({msg.author.id}){author_status}のメッセージ", icon_url=msg.author.display_avatar.replace(static_format="png").url) 49 | 50 | post_time = msg.created_at.strftime("%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}").format(*"年月日時分秒") 51 | 52 | if msg.edited_at: 53 | edit_time = msg.edited_at.strftime("%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}").format(*"年月日時分秒") 54 | else: 55 | edit_time = "なし" 56 | 57 | e.set_footer(text=f"メッセージ送信時間:{post_time}/最終編集時間:{edit_time}") 58 | 59 | e.add_field(name="含まれる埋め込みの数",value=f"{len(msg.embeds)}個", inline=False) 60 | attach_url = '\n'.join([f'{i.url}\n' for i in msg.attachments]) 61 | e.add_field(name="含まれる添付ファイルの数",value=f"{len(msg.attachments)}個\n{attach_url.replace('cdn.discordapp.com','media.discordapp.net')}", inline=False) 62 | e.add_field(name="システムメッセージかどうか",value=msg.is_system(), inline=False) 63 | 64 | 65 | if isinstance(msg.channel, discord.TextChannel): 66 | if msg.guild.rules_channel and msg.channel.id == msg.guild.rules_channel.id: 67 | chtype = f"{msg.channel.name}({msg.channel.id}):ルールチャンネル" 68 | elif msg.channel.is_news(): 69 | chtype = f"{msg.channel.name}({msg.channel.id}):アナウンスチャンネル" 70 | else: 71 | chtype = f"{msg.channel.name}({msg.channel.id}):テキストチャンネル" 72 | elif isinstance(msg.channel, discord.VoiceChannel): 73 | chtype = f"{msg.channel.name}({msg.channel.id}):ボイスチャンネル内テキストチャット" 74 | elif isinstance(msg.channel, discord.Thread): 75 | chtype = f"{msg.channel.name}({msg.channel.id}):テキストチャンネル内スレッド" 76 | else: 77 | logging.warning( 78 | f"Unexpected channel type: {type(msg.channel)}, " 79 | f"Channel ID: {getattr(msg.channel, 'id', 'Unknown ID')}, " 80 | f"Channel Name: {getattr(msg.channel, 'name', 'Unknown Name')}" 81 | ) 82 | chtype = "Unknown channel type" 83 | e.add_field(name="メッセージの送信先チャンネル", value=chtype, inline=False) 84 | 85 | if msg.type == discord.MessageType.reply: 86 | e.add_field(name="メッセージへの返信等", value=f"返信元確認用:`{msg.reference.channel_id}-{msg.reference.message_id}`", inline=False) 87 | 88 | e.add_field(name="メンションの内訳", value=f"全員あてメンション:{msg.mention_everyone}\nユーザーメンション:{len(msg.mentions)}個\n役職メンション:{len(msg.role_mentions)}個\nチャンネルメンション:{len(msg.channel_mentions)}個", inline=False) 89 | e.add_field(name="メッセージID", value=str(msg.id), inline=False) 90 | if msg.webhook_id: 91 | e.add_field(name="Webhook投稿", value=f"ID:{msg.webhook_id}", inline=False) 92 | e.add_field(name="ピン留めされているかどうか", value=str(msg.pinned), inline=False) 93 | if msg.reactions: 94 | e.add_field(name="リアクション",value=",".join([f"{r.emoji}:{r.count}" for r in msg.reactions]), inline=False) 95 | e.add_field(name="メッセージのフラグ", value=[i[0] for i in iter(msg.flags) if i[1]], inline=False) 96 | 97 | e.add_field(name="このメッセージに飛ぶ", value=msg.jump_url, inline=False) 98 | 99 | await interaction.response.send_message(embed=e, ephemeral=True) 100 | 101 | bot.tree.add_command(message_info) 102 | 103 | 104 | 105 | 106 | async def setup(bot): 107 | await bot.add_cog(m10s_messageinfo(bot)) 108 | -------------------------------------------------------------------------------- /cogs/m10s_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | import datetime 7 | from dateutil.relativedelta import relativedelta as rdelta 8 | import traceback 9 | from typing import Union 10 | 11 | import config as cf 12 | 13 | import m10s_util as ut 14 | 15 | from my_module import dpy_interaction as dpyui 16 | 17 | from discord import app_commands 18 | 19 | 20 | class info(commands.Cog): 21 | 22 | def __init__(self, bot): 23 | self.bot = bot 24 | 25 | @commands.command(name="dguild") 26 | @ut.runnable_check() 27 | async def serverinfo(self, ctx, sid=None): 28 | if sid is not None: 29 | sevinfo: discord.Guild = self.bot.get_guild(int(str(sid))) 30 | else: 31 | sevinfo: discord.Guild = ctx.message.guild 32 | 33 | if sevinfo is None: 34 | return await ctx.send("そのサーバーに思惟奈ちゃんがいるかどうか確認してください。") 35 | 36 | try: 37 | embed = discord.Embed(title=await ctx._( 38 | "serverinfo-name"), description=sevinfo.name, color=self.bot.ec) 39 | if sevinfo.icon is not None: 40 | embed.set_thumbnail( 41 | url=sevinfo.icon.replace(static_format='png')) 42 | embed.add_field(name=await ctx._("serverinfo-role"), 43 | value=len(sevinfo.roles)) 44 | embed.add_field(name=await ctx._("serverinfo-emoji"), 45 | value=len(sevinfo.emojis)) 46 | 47 | bm = 0 48 | ubm = 0 49 | for m in sevinfo.members: 50 | if m.bot: 51 | bm = bm + 1 52 | else: 53 | ubm = ubm + 1 54 | embed.add_field(name=await ctx._("serverinfo-member"), 55 | value=f"{len(sevinfo.members)}(bot:{bm}/user:{ubm})") 56 | embed.add_field(name=await ctx._("serverinfo-channel"), 57 | value=f'{await ctx._("serverinfo-text")}:{len(sevinfo.text_channels)}\n{await ctx._("serverinfo-voice")}:{len(sevinfo.voice_channels)}') 58 | embed.add_field(name=await ctx._("serverinfo-id"), value=sevinfo.id) 59 | embed.add_field(name=await ctx._("serverinfo-owner"), 60 | value=sevinfo.owner.name) 61 | embed.add_field(name=await ctx._("serverinfo-create"), value=(sevinfo.created_at + rdelta( 62 | hours=9)).strftime('%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')) 63 | rlist = ",".join([i.name for i in sevinfo.roles]) 64 | if len(rlist) <= 1000: 65 | embed.add_field(name=await ctx._("serverinfo-roles"), value=rlist) 66 | try: 67 | embed.add_field(name=await ctx._("serverinfo-nitroboost"), 68 | value=await ctx._("serverinfo-nitroboost-val", sevinfo.premium_tier)) 69 | embed.add_field(name=await ctx._("serverinfo-nitroboost-can-title"), value=await ctx._( 70 | f"serverinfo-nitroboost-can-{sevinfo.premium_tier}", sevinfo.premium_tier, sevinfo.premium_subscription_count)) 71 | except: 72 | pass 73 | 74 | if sevinfo.system_channel: 75 | embed.add_field(name=await ctx._("serverinfo-sysch"), 76 | value=sevinfo.system_channel) 77 | try: 78 | embed.add_field(name=await ctx._("serverinfo-sysch-welcome"), 79 | value=sevinfo.system_channel_flags.join_notifications) 80 | embed.add_field(name=await ctx._("serverinfo-sysch-boost"), 81 | value=sevinfo.system_channel_flags.premium_subscriptions) 82 | except: 83 | pass 84 | if sevinfo.afk_channel: 85 | embed.add_field(name=await ctx._("serverinfo-afkch"), 86 | value=sevinfo.afk_channel.name) 87 | embed.add_field(name=await ctx._("serverinfo-afktimeout"), 88 | value=str(sevinfo.afk_timeout/60)) 89 | await ctx.send(embed=embed) 90 | except Exception as e: 91 | await ctx.send(e) 92 | # await ctx.send(await ctx._("serverinfo-except")) 93 | 94 | #chinfo is 'm10s_chinfo_rewrite' now 95 | 96 | 97 | @commands.hybrid_command(name="team_sina-chan", description="チーム☆思惟奈ちゃんメンバーを表示します。") 98 | @ut.runnable_check() 99 | @ut.runnable_check_for_appcmd() 100 | async def view_teammember(self, ctx): 101 | await ctx.send(embed=ut.getEmbed(await ctx._("team_sina-chan"), "\n".join([(await self.bot.fetch_user(i)).name for i in self.bot.team_sina]))) 102 | 103 | @commands.command() 104 | @ut.runnable_check() 105 | async def mutual_guilds(self, ctx, uid=None): 106 | if ctx.author.id in self.bot.team_sina: 107 | try: 108 | user = await self.bot.fetch_user(int(uid)) 109 | except: 110 | user = ctx.author 111 | mg = [] 112 | for g in self.bot.guilds: 113 | if g.get_member(user.id): 114 | mg += [f"{g.name}({g.id})"] 115 | if mg != []: 116 | t = "\n".join(mg) 117 | e = discord.Embed(description=f"```{t}```", color=self.bot.ec) 118 | e.set_author(name=f"思惟奈ちゃんと{user}の共通サーバー") 119 | await ctx.send(embed=e) 120 | else: 121 | e = discord.Embed(description="なし", color=self.bot.ec) 122 | e.set_author(name=f"思惟奈ちゃんと{user}の共通サーバー") 123 | await ctx.send(embed=e) 124 | else: 125 | await ctx.reply("> 共通サーバーチェッカー\n Discord公式の機能でチェックできるようになったため、このコマンドは運営専用になりました。プロフィールから確認してください。") 126 | 127 | 128 | async def setup(bot): 129 | await bot.add_cog(info(bot)) 130 | -------------------------------------------------------------------------------- /l10n.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import discord 4 | from discord.ext import commands 5 | 6 | LOCALES = [lang[:-5] for lang in os.listdir("lang") if lang.endswith(".json")] 7 | 8 | 9 | class MissingTranslation(Exception): 10 | pass 11 | 12 | 13 | class TranslateHandler: 14 | def __init__(self, bot, supported_locales=LOCALES): 15 | self.bot = bot 16 | self.supported_locales = supported_locales 17 | self._translation_cache = {} 18 | self._lang_cache = {} 19 | 20 | def get_string_for(self, lang, key, placeholder=""): 21 | if lang not in self.supported_locales: 22 | return placeholder 23 | if self._translation_cache and lang in self._translation_cache and key in self._translation_cache[lang]: 24 | return self._translation_cache[lang][key] 25 | try: 26 | with open(f"lang/{lang}.json", "r", encoding="utf-8") as f: 27 | dic = json.load(f) 28 | self.cache_with_same_key(lang, key, dic) 29 | value = dic.get(key, None) 30 | if value: 31 | return value 32 | raise MissingTranslation 33 | except (FileNotFoundError, MissingTranslation): 34 | if lang == "ja": 35 | return placeholder 36 | else: 37 | return self.get_string_for("ja", key, placeholder) 38 | 39 | def cache_with_same_key(self, lang, key, f): 40 | if lang not in self._translation_cache: 41 | self._translation_cache[lang] = {} 42 | key_name = key.split("-")[0] 43 | keys = [file_key for file_key in f.keys( 44 | ) if file_key.startswith(key_name)] 45 | for cache_key in keys: 46 | self._translation_cache[cache_key] = f[cache_key] 47 | 48 | def clean_cache(self): 49 | self._translation_cache = {} 50 | self._lang_cache = {} 51 | 52 | async def update_language_cache(self, target, to=None): 53 | if to is None: 54 | if isinstance(target, discord.Guild): 55 | to = await self.get_lang_by_guild(target, cache=False) 56 | else: 57 | to = await self.get_lang_by_user(target, cache=False) 58 | return # get function caches 59 | if to not in self.supported_locales: 60 | return 61 | self._lang_cache[target.id] = to 62 | 63 | async def get_lang_by_guild(self, guild, cache=True): 64 | if cache and guild.id in self._lang_cache: 65 | return self._lang_cache[guild.id] 66 | guild_db = await self.bot.cursor.fetchone("SELECT lang FROM guilds WHERE id=%s", (guild.id,)) 67 | #guild_db = await self.bot.cursor.fetchone() 68 | lang = None 69 | if not guild_db: 70 | lang = guild.preferred_locale 71 | elif not guild_db["lang"]: 72 | lang = guild.preferred_locale 73 | else: 74 | lang = guild_db["lang"] 75 | if lang in self.supported_locales: 76 | self._lang_cache[guild.id] = lang 77 | return lang 78 | if lang in self.supported_locales: 79 | return lang 80 | return None 81 | 82 | async def get_lang_by_user(self, user, cache=True): 83 | if cache and user.id in self._lang_cache: 84 | return self._lang_cache[user.id] 85 | user_db = await self.bot.cursor.fetchone( 86 | "SELECT lang FROM users WHERE id = %s", (user.id,)) 87 | #user_db = await self.bot.cursor.fetchone() 88 | if user_db and user_db["lang"] and user_db["lang"] in self.supported_locales: 89 | self._lang_cache[user.id] = user_db["lang"] 90 | return user_db["lang"] 91 | return None 92 | 93 | async def get_lang(self, user): 94 | lang = await self.get_lang_by_user(user) 95 | if lang: 96 | return lang 97 | if isinstance(user, discord.Member): 98 | lang = await self.get_lang_by_guild(user.guild) 99 | if lang: 100 | return lang 101 | return "ja" 102 | 103 | async def get_translation_for(self, user, key, *args, **kwargs): 104 | return self.get_raw_translation(await self.get_lang(user), key, *args, **kwargs) 105 | 106 | async def get_guild_translation_for(self, guild, key, *args, **kwargs): 107 | lang = await self.get_lang_by_guild(guild) 108 | if not lang: 109 | lang = "ja" 110 | return self.get_raw_translation(lang, key, *args, **kwargs) 111 | 112 | def get_raw_translation(self, lang, key, *args, **kwargs): 113 | word = self.get_string_for(lang, key) 114 | if isinstance(word, list): 115 | return word 116 | return word.format(*args, **kwargs) 117 | 118 | async def get_any_translation(self, target, key, *args, **kwargs): 119 | if isinstance(target, discord.abc.User): 120 | return await self.get_translation_for(target, key, *args, **kwargs) 121 | elif isinstance(target, discord.Guild): 122 | return await self.get_guild_translation_for(target, key, *args, **kwargs) 123 | return self.get_raw_translation(target, key, *args, **kwargs) 124 | 125 | 126 | class LocalizedContext(commands.Context): 127 | async def say(self, key, *args, **kwargs): 128 | return await self.send(await self._(key, *args, **kwargs)) 129 | 130 | async def _(self, key, *args, **kwargs): 131 | return await self.bot.translate_handler.get_translation_for(self.author, key, *args, **kwargs) 132 | 133 | async def l10n(self, user, key, *args, **kwargs): 134 | return await self.bot.translate_handler.get_translation_for(user, key, *args, **kwargs) 135 | 136 | async def l10n_guild(self, guild, key, *args, **kwargs): 137 | return await self.bot.translate_handler.get_guild_translation_for(guild, key, *args, **kwargs) 138 | 139 | async def l10n_any(self, target, key, *args, **kwargs): 140 | return await self.bot.translate_handler.get_any_translation(target, key, *args, **kwargs) 141 | 142 | def l10n_raw(self, lang, key, *args, **kwargs): 143 | return self.bot.translate_handler.get_raw_translation(lang, key, *args, **kwargs) 144 | 145 | async def user_lang(self, user=None): 146 | if user is None: 147 | user = self.author 148 | return await self.bot.translate_handler.get_lang_by_user(user) 149 | -------------------------------------------------------------------------------- /cogs/m10s_remainder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- # 2 | 3 | from typing import Optional, Union 4 | import discord 5 | from discord.ext import commands, tasks 6 | import datetime 7 | import time 8 | 9 | from discord import app_commands 10 | 11 | import m10s_util as ut 12 | 13 | import asyncio 14 | 15 | class m10s_remainder(commands.Cog): 16 | 17 | def __init__(self, bot): 18 | self.bot = bot 19 | self.remainder_check.start() 20 | 21 | @commands.hybrid_group(description="リマインダー機能") 22 | @ut.runnable_check() 23 | async def remainder(self,ctx): 24 | pass 25 | 26 | @remainder.command(description="リマインダーを作成します。") 27 | @app_commands.describe(send_text="リマインダーの文面") 28 | @app_commands.describe(mention_at="メンションする役職") 29 | @ut.runnable_check() 30 | @ut.runnable_check_for_appcmd() 31 | async def set(self,ctx,send_text:str, mention_at:Optional[discord.Role]): 32 | rid = int(time.time()) 33 | if mention_at: 34 | mention_at = mention_at.id 35 | await ctx.send("> リマインダー\n 日時を`2020/12/1 22:00`の形式で入力してください。") 36 | m = await self.bot.wait_for("message",check=lambda m:m.author.id == ctx.message.author.id) 37 | try: 38 | ts = datetime.datetime.strptime(m.content, "%Y/%m/%d %H:%M") 39 | except: 40 | await ctx.send("> リマインダー\n 日時の指定が誤っています。もう一度やり直してください。") 41 | return 42 | await self.bot.cursor.execute("insert into remaind (id,stext,mention_role,time,chid) values (%s,%s,%s,%s,%s)", (rid, send_text, mention_at, ts.timestamp(), ctx.channel.id)) 43 | await ctx.send(f"> リマインダー\n {ts.strftime('%Y/%m/%d %H:%M')}にリマインダーの登録をしました。(リマインダーID:`{rid}`)") 44 | 45 | @remainder.command(description="リマインダーの確認をします。") 46 | @app_commands.describe(rid="リマインダーのID") 47 | @ut.runnable_check() 48 | @ut.runnable_check_for_appcmd() 49 | async def check(self,ctx,rid:Optional[int]): 50 | if rid: 51 | i = await self.bot.cursor.fetchone("select * from remaind where id = %s",(int(rid),)) 52 | #i = await self.bot.cursor.fetchone() 53 | if i: 54 | e=discord.Embed(title="リマインド情報",color=self.bot.ec) 55 | try: 56 | e.add_field(name="メンションする役職のID",value=f"{i['mention_role'] or '(役職なし)'}") 57 | except: 58 | pass 59 | e.add_field(name="time",value=f"{datetime.datetime.fromtimestamp(i['time']).strftime('%Y/%m/%d %H:%M')}") 60 | e.add_field(name="テキスト",value=f"{i['stext']}") 61 | e.add_field(name="送信先チャンネル",value=f"<#{i['chid']}>") 62 | await ctx.send("> リマインダー\n 該当IDのリマインドが見つかりました!",embed=e) 63 | else: 64 | await ctx.send("> リマインダー\n 該当IDのリマインドは見つかりませんでした。") 65 | else: 66 | i = await self.bot.cursor.fetchall("select * from remaind where chid = %s",(ctx.channel.id,)) 67 | #i = await self.bot.cursor.fetchall() 68 | if i: 69 | pmax = len(i)-1 70 | page = 0 71 | 72 | def get_page(page): 73 | e=discord.Embed(title="リマインド情報",color=self.bot.ec) 74 | try: 75 | e.add_field(name="メンションする役職のID",value=f"{i[page]['mention_role'] or '(役職なし)'}") 76 | except: 77 | pass 78 | e.add_field(name="time",value=f"{datetime.datetime.fromtimestamp(i[page]['time']).strftime('%Y/%m/%d %H:%M')}") 79 | e.add_field(name="テキスト",value=f"{i[page]['stext']}") 80 | e.add_field(name="送信先チャンネル",value=f"<#{i[page]['chid']}>") 81 | e.add_field(name="リマインダーID",value=i[page]['id']) 82 | e.set_footer(text=f"page:{page+1}/{pmax+1}") 83 | return e 84 | 85 | msg = await ctx.send(embed=get_page(page)) 86 | await msg.add_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041)) 87 | await msg.add_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216)) 88 | 89 | while True: 90 | try: 91 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.message.author.id, timeout=30) 92 | except: 93 | break 94 | try: 95 | await msg.remove_reaction(r, u) 96 | except: 97 | pass 98 | if str(r) == str(self.bot.create_emoji_str('s_move_right',653161518170505216)): 99 | if page == pmax: 100 | page = 0 101 | else: 102 | page = page + 1 103 | elif str(r) == str(self.bot.create_emoji_str("s_move_left",653161518195671041)): 104 | if page == 0: 105 | page = pmax 106 | else: 107 | page = page - 1 108 | await msg.edit(embed=get_page(page)) 109 | else: 110 | await ctx.reply("> リマインダー\n このチャンネルには、まだ実行時間前のリマインダーはありません。") 111 | 112 | 113 | @remainder.command(description="リマインダーを削除します。") 114 | @app_commands.describe(rid="リマインダーのID") 115 | @ut.runnable_check() 116 | @ut.runnable_check_for_appcmd() 117 | async def delete(self,ctx,rid:int): 118 | await self.bot.cursor.execute("delete from remaind where id = %s",(rid,)) 119 | await ctx.send("> リマインダー\n 該当IDを持ったリマインドがある場合、それを削除しました。") 120 | 121 | 122 | 123 | @tasks.loop(seconds=2) 124 | async def remainder_check(self): 125 | now = datetime.datetime.now() 126 | remainds = await self.bot.cursor.fetchall("select * from remaind") 127 | #remainds = await self.bot.cursor.fetchall() 128 | remaind_list = [i for i in remainds if datetime.datetime.fromtimestamp(i['time'])<= now] 129 | for i in remaind_list: 130 | ch = self.bot.get_channel(i["chid"]) 131 | try: 132 | role = ch.guild.get_role(i["mention_role"]) 133 | except: 134 | role = None 135 | try: 136 | if ch.guild.id == 574170788165582849: 137 | await ch.send(f"> リマインド {role.mention if role else ''}\n {i['stext']}",allowed_mentions=discord.AllowedMentions(everyone=True, users=True, roles=True)) 138 | else: 139 | await ch.send(f"> リマインド {role.mention if role else ''}\n {i['stext']}") 140 | except: 141 | print("失敗したリマインダー") 142 | finally: 143 | await self.bot.cursor.execute("delete from remaind where id = %s",(i["id"],)) 144 | 145 | 146 | async def setup(bot): 147 | await bot.add_cog(m10s_remainder(bot)) -------------------------------------------------------------------------------- /cogs/m10s_set_activity_roles.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | from typing import Optional 7 | 8 | from discord import app_commands 9 | 10 | import m10s_util as ut 11 | """↑様々な便利コマンド詰め合わせ 12 | ut.ondevicon(Member) 13 | オンライン状況に基づくデバイスアイコンテキストを返す。 14 | ut.getEmbed(title,description,color,(name,value)...) 15 | Embedのお手軽生成。これ使ったのがあるから消そうにも消せない。 16 | await ut.opendm(Member/User) 17 | DMチャンネルを返します。DMチャンネルが存在しないなんてことでは困らせません。 18 | await wait_message_return(ctx,質問するテキスト,←の送信先,待つ時間): 19 | 入力待ちの簡略化。タイムアウトの例外キャッチを忘れずに 20 | ut.get_vmusic(bot,member) 21 | 思惟奈ちゃんの音楽再生機能でそのメンバーがきいている曲を返します。 22 | 23 | メモ: 24 | unknown = -1 25 | playing = 0 26 | streaming = 1 27 | listening = 2 28 | watching = 3 29 | custom = 4 30 | competing = 5 31 | 32 | bot.cursor.execute( 33 | "CREATE TABLE IF NOT EXISTS actrole_optin(id integer PRIMARY KEY NOT NULL, is_enable integer NOT NULL default 0);") 34 | 35 | await self.bot.cursor.execute("INSERT INTO activity_roles(guild_id,activity_type,role_id) VALUES(%s,%s,%s)",(ctx.guild.id,act_id,role_id)) 36 | 37 | 38 | await self.bot.cursor.execute( 39 | "CREATE TABLE IF NOT EXISTS activity_roles(guild_id integer PRIMARY KEY NOT NULL,activity_type integer NOT NULL,role_id integer);") 40 | 41 | await self.bot.cursor.execute("UPDATE activity_roles SET role_id = %s WHERE guild_id = %s AND activity_type = %s",(role_id,ctx.guild.id,act_id)) 42 | 43 | await self.bot.cursor.execute("select * from activity_roles where guild_id = %s AND activity_type = %s", (ctx.guild.id,act_id)) 44 | gpf = await self.bot.cursor.fetchone() 45 | """ 46 | 47 | class m10s_act_role(commands.Cog): 48 | 49 | def __init__(self, bot): 50 | self.bot = bot 51 | self.cvtdict = { 52 | -1:"unknown", 53 | 0:"playing", 54 | 1:"streaming", 55 | 2:"listening", 56 | 3:"watching", 57 | 4:"custom", 58 | 5:"competing" 59 | } 60 | 61 | @commands.hybrid_command(name="activity_role_setting", description="プレイ中ステータスに応じた役職付与を受け付けるかどうか") 62 | @app_commands.describe(is_enable="有効化するかどうか") 63 | @ut.runnable_check() 64 | @ut.runnable_check_for_appcmd() 65 | async def actrole_set(self, ctx, is_enable:bool): 66 | await self.bot.cursor.execute("UPDATE actrole_optin SET is_enable = %s WHERE id = %s",(int(is_enable),ctx.author.id)) 67 | await ctx.reply(f"プレイ中に応じた役職設定を、{is_enable}に設定したよ!") 68 | 69 | @commands.hybrid_command(aliases=["prole"], description="特定アクティビティタイプで付与する役職設定") 70 | @app_commands.describe(activity_type="検出するアクティビティタイプ") 71 | @app_commands.describe(role="付与するロール") 72 | @discord.app_commands.choices(activity_type=[ 73 | discord.app_commands.Choice(name="unknown(不明)", value=-1), 74 | discord.app_commands.Choice(name="playing(プレイ中)", value=0), 75 | discord.app_commands.Choice(name="streaming(配信中)", value=1), 76 | discord.app_commands.Choice(name="listening(再生中)", value=2), 77 | discord.app_commands.Choice(name="watching(視聴中)", value=3), 78 | discord.app_commands.Choice(name="custom(カスタムステータス)", value=4), 79 | discord.app_commands.Choice(name="competing(競争中)", value=5) 80 | ]) 81 | @ut.runnable_check() 82 | @ut.runnable_check_for_appcmd() 83 | @app_commands.default_permissions(administrator=True) 84 | @commands.has_permissions(administrator=True) 85 | @app_commands.checks.has_permissions(administrator=True) 86 | async def playing_roles(self, ctx, activity_type:int, role:Optional[discord.Role]): 87 | try: 88 | act_id = self.cvtdict.get(activity_type,None) 89 | if act_id is None: 90 | try: 91 | await ctx.reply("> エラー\n アクティビティタイプが不明です。`s-help prole`を参考にしてください。") 92 | except: 93 | await ctx.send("> エラー\n アクティビティタイプが不明です。`s-help prole`を参考にしてください。") 94 | else: 95 | rtn = await self.bot.cursor.fetchone("select * from activity_roles where guild_id = %s AND activity_type = %s", (ctx.guild.id,activity_type)) 96 | # await self.bot.cursor.execute() 97 | if rtn: 98 | if role: 99 | await self.bot.cursor.execute("UPDATE activity_roles SET role_id = %s WHERE guild_id = %s AND activity_type = %s",(role.id,ctx.guild.id,activity_type)) 100 | else: 101 | await self.bot.cursor.execute("DELETE FROM activity_roles WHERE guild_id = %s AND activity_type = %s",(ctx.guild.id,activity_type)) 102 | else: 103 | if role: 104 | await self.bot.cursor.execute("INSERT INTO activity_roles(guild_id,activity_type,role_id) VALUES(%s,%s,%s)",(ctx.guild.id,activity_type,role.id)) 105 | try: 106 | await ctx.reply("> アクティビティロール\n 正常に登録/更新/削除が完了しました。") 107 | except: 108 | await ctx.send("> アクティビティロール\n 正常に登録/更新/削除が完了しました。") 109 | except Exception as e: 110 | await ctx.send(f"```{e}```") 111 | 112 | 113 | # @commands.Cog.listener() 114 | async def on_presence_update(self, b,a): 115 | try: 116 | gpf = await self.bot.cursor.fetchone("select * from actrole_optin where id = %s", (b.id,)) 117 | #gpf = await self.bot.cursor.fetchone() 118 | if gpf and gpf["is_enable"] == 1: 119 | if not b.activity == a.activity: 120 | if b.activity and (a.activity is None): 121 | bact = await self.bot.cursor.fetchone("select role_id from activity_roles where guild_id = %s AND activity_type = %s", (b.guild.id,int(b.activity.type))) 122 | #bact = await self.bot.cursor.fetchone() 123 | await b.remove_roles(b.guild.get_role(bact["role_id"])) 124 | elif (b.activity is None) and a.activity: 125 | aact = await self.bot.cursor.fetchone("select role_id from activity_roles where guild_id = %s AND activity_type = %s", (a.guild.id,int(a.activity.type))) 126 | #aact = await self.bot.cursor.fetchone() 127 | await a.add_roles(a.guild.get_role(aact["role_id"])) 128 | elif b.activity and a.activity: 129 | bact = await self.bot.cursor.fetchone("select role_id from activity_roles where guild_id = %s AND activity_type = %s", (b.guild.id,int(b.activity.type))) 130 | #bact = await self.bot.cursor.fetchone() 131 | await b.remove_roles(b.guild.get_role(bact["role_id"])) 132 | aact = await self.bot.cursor.fetchone("select role_id from activity_roles where guild_id = %s AND activity_type = %s", (a.guild.id,int(a.activity.type))) 133 | #aact = await self.bot.cursor.fetchone() 134 | await a.add_roles(a.guild.get_role(aact["role_id"])) 135 | except: 136 | pass 137 | 138 | async def setup(bot): 139 | await bot.add_cog(m10s_act_role(bot)) 140 | -------------------------------------------------------------------------------- /cogs/apple_invite.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands, tasks 3 | from discord import app_commands 4 | 5 | import m10s_util as ut 6 | 7 | class AppleInviteCog(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.db = bot.cursor 11 | self._lock = False 12 | 13 | async def get_log_channel(self, guild_id): 14 | guild = await self.db.fetchone("SELECT * FROM guilds WHERE id=%s", (guild_id,)) 15 | #guild = await self.bot.cursor.fetchone() 16 | if not guild: 17 | return None 18 | if guild["sendlog"]: 19 | return self.bot.get_channel(guild["sendlog"]) 20 | return None 21 | 22 | async def delete_invite(self, code): 23 | await self.db.execute("DELETE FROM invites WHERE id = %s", (code,)) 24 | 25 | async def add_invite(self, invite): 26 | # Validate the Invite. If those are missing, we have to return. 27 | if not all(( 28 | invite, 29 | hasattr(invite, "code"), 30 | invite.code, 31 | hasattr(invite, "guild"), 32 | invite.guild, 33 | isinstance(invite.guild, discord.Guild) 34 | )): 35 | return 36 | 37 | if not await self.get_log_channel(invite.guild.id): 38 | return 39 | 40 | if not self.bot.apple_util.has_all_perms( 41 | invite.guild.me, 42 | None, 43 | 'manage_guild', 44 | 'create_instant_invite', 45 | 'manage_channels' 46 | ): 47 | return 48 | 49 | # If inviter is missing, refetch. 50 | if not all(( 51 | hasattr(invite, "inviter"), 52 | invite.inviter, 53 | isinstance(invite.inviter, discord.abc.User) 54 | )): 55 | invites = await invite.guild.invites() 56 | invite = discord.utils.get(invites, code=invite.code) 57 | 58 | await self.db.execute("INSERT INTO invites values (%s, %s, 0, %s)", ( 59 | invite.code, 60 | invite.guild.id, 61 | invite.inviter.id 62 | )) 63 | 64 | async def sync_invites(self): 65 | p = await self.bot.cursor.fetchall("SELECT id FROM guilds WHERE sendlog IS NOT NULL") 66 | guilds = [ 67 | self.bot.get_guild(g["id"]) 68 | for g 69 | in p 70 | if self.bot.get_guild(g["id"]) 71 | ] 72 | touched_invites = set() 73 | for guild in guilds: 74 | if not self.bot.apple_util.has_all_perms( 75 | guild.me, 76 | None, 77 | "manage_guild", 78 | "create_instant_invite", 79 | "manage_channels" 80 | ): 81 | continue 82 | invites = await guild.invites() 83 | touched_invites |= set(i.code for i in invites) 84 | 85 | for invite in invites: 86 | a = await self.bot.cursor.fetchone("SELECT id FROM invites WHERE id = %s", (invite.code,)) 87 | if a: 88 | # invite exists, updating 89 | await self.db.execute( 90 | "UPDATE invites SET uses = %s WHERE id = %s", (invite.uses, invite.code)) 91 | else: 92 | await self.add_invite(invite) 93 | b = await self.bot.cursor.fetchall("SELECT id FROM invites") 94 | invites_in_db = set(i["id"] for i in b) 95 | needs_deletion = invites_in_db.difference(touched_invites) 96 | [await self.delete_invite(i) for i in needs_deletion] 97 | 98 | @commands.Cog.listener() 99 | async def on_invite_delete(self, invite): 100 | await self.delete_invite(invite.code) 101 | 102 | @commands.Cog.listener() 103 | async def on_invite_create(self, invite): 104 | await self.add_invite(invite) 105 | 106 | @commands.Cog.listener() 107 | async def on_ready(self): 108 | await self.sync_invites() 109 | 110 | @commands.Cog.listener() 111 | async def on_member_join(self, member): 112 | self._lock = True 113 | guild = member.guild 114 | log_ch = await self.get_log_channel(guild.id) 115 | if not log_ch: 116 | self._lock = False 117 | return 118 | used_invites = [] 119 | invites = await guild.invites() 120 | invites_in_db = await self.db.fetchall("SELECT * FROM invites WHERE guild_id = %s", (guild.id,)) 121 | #invites_in_db = await self.bot.cursor.fetchall() 122 | for invite in invites: 123 | invite_db = discord.utils.find( 124 | lambda i: i["id"] == invite.code, invites_in_db) 125 | if invite.uses > invite_db["uses"]: 126 | used_invites = [{ 127 | "code": invite.code, 128 | "inviter": invite.inviter 129 | }] 130 | await self.db.execute( 131 | "UPDATE invites SET uses = %s WHERE id = %s", (invite.uses, invite.code)) 132 | break 133 | if not used_invites: 134 | await self.bot.cursor.execute("SELECT id FROM invites") 135 | db_set = set(i["id"] for i in await self.bot.cursor.fetchall()) 136 | api_set = set(i.code for i in invites) 137 | diff = db_set.difference(api_set) 138 | used_invites = [ 139 | {"code": code, "inviter": self.bot.get_user(discord.utils.find( 140 | lambda i: i["id"] == code, invites_in_db)["inviter"])} 141 | for code 142 | in diff 143 | ] 144 | _ = [await self.delete_invite(i) for i in diff] 145 | self._lock = False 146 | self.bot.dispatch('member_join_with_invites', member, used_invites) 147 | 148 | @tasks.loop(minutes=10) 149 | async def invite_checker(self): 150 | if self._lock: 151 | return 152 | await self.sync_invites() 153 | 154 | @commands.Cog.listener() 155 | async def on_error(self, event, *args, **kwargs): 156 | if event == "on_member_join": 157 | self._lock = False 158 | 159 | @commands.Cog.listener() 160 | async def on_member_join_with_invites(self, member, invites): 161 | log_ch = await self.get_log_channel(member.guild.id) 162 | e = discord.Embed(title=f"{str(member)}の招待の情報", 163 | description=str(member.id)) 164 | e.set_thumbnail(url=str(member.avatar)) 165 | for i in invites: 166 | e.add_field( 167 | name=f"招待はこれかも? {i['code']}", value=f"{str(i['inviter'])} - {i['inviter'].id}") 168 | await log_ch.send(embed=e) 169 | 170 | @commands.command(hidden=True) 171 | @commands.guild_only() 172 | @app_commands.allowed_contexts(guilds=True, dms=False, private_channels=False) 173 | @ut.runnable_check() 174 | @ut.runnable_check_for_appcmd() 175 | @app_commands.default_permissions(manage_guild=True, create_instant_invite=True) 176 | @commands.has_permissions(manage_guild=True, create_instant_invite=True) 177 | @app_commands.checks.has_permissions(manage_guild=True, create_instant_invite=True) 178 | async def checkoffline(self, ctx): 179 | self._lock = True 180 | i = await ctx.channel.create_invite(max_uses=1, max_age=0, reason="temporary invite") 181 | i2 = await self.bot.fetch_invite(i.code) 182 | pc = i2.approximate_presence_count 183 | await i.delete() 184 | online = len( 185 | [m for m in ctx.guild.members if m.status is not discord.Status.offline and await self.bot.can_use_online(m)]) 186 | await ctx.author.send(f"オンライン隠し: {pc - online}人") 187 | 188 | 189 | async def setup(bot): 190 | await bot.add_cog(AppleInviteCog(bot)) 191 | -------------------------------------------------------------------------------- /cogs/m10s_search.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import json 6 | import random 7 | from googleapiclient.discovery import build 8 | import wikipedia 9 | import time 10 | import asyncio 11 | from dateutil.relativedelta import relativedelta as rdelta 12 | 13 | from discord import app_commands 14 | 15 | import m10s_util as ut 16 | 17 | import config as cf 18 | 19 | import traceback 20 | 21 | class search(commands.Cog): 22 | 23 | def __init__(self, bot): 24 | self.bot = bot 25 | 26 | @commands.command() 27 | @ut.runnable_check() 28 | @ut.runnable_check_for_appcmd() 29 | async def getby(self, ctx, k: str): 30 | await ctx.send(embed=ut.getEmbed("", await ctx._(k))) 31 | 32 | @commands.hybrid_group(name="search", description="検索系コマンド") 33 | @ut.runnable_check() 34 | @ut.runnable_check_for_appcmd() 35 | async def search_commands(self,ctx): 36 | pass 37 | 38 | """ -> twitter APIの仕様変更によるもの 39 | @search_commands.command(aliases=["twisearch", "twitterで検索して"], description="Twitter検索") 40 | @app_commands.describe(word="検索文字列") 41 | @commands.cooldown(1, 15, type=commands.BucketType.user) 42 | @ut.runnable_check() 43 | async def twitter(self, ctx, *, word: str): 44 | try: 45 | async with ctx.message.channel.typing(): 46 | ret = self.bot.twi.search.tweets( 47 | q=word, result_type="recent", lang="ja", count=1) 48 | tweet = ret["statuses"][0] 49 | embed = discord.Embed(description=tweet["text"], color=int( 50 | tweet["user"]["profile_background_color"], 16)) 51 | embed.set_author(name=f'{tweet["user"]["name"]}(@{tweet["user"]["screen_name"]})', 52 | url=f'https://twitter.com/{tweet["user"]["screen_name"]}', icon_url=tweet["user"]["profile_image_url_https"]) 53 | try: 54 | embed.set_image( 55 | url=tweet["entities"]["media"][0]["media_url_https"]) 56 | except: 57 | pass 58 | embed.add_field(name=await ctx._( 59 | "twi-see"), value=f'{self.bot.get_emoji(653161518451392512)} https://twitter.com/{tweet["user"]["screen_name"]}/status/{tweet["id"]}') 60 | await ctx.send(embed=embed) 61 | except: 62 | await ctx.send(await ctx._("twi-error")) 63 | # await ctx.send(embed=ut.getEmbed("traceback",traceback.format_exc(3)))""" 64 | 65 | @search_commands.command(aliases=["jwp", "次の言葉でwikipedia調べて"],description="wikipedia検索") 66 | @app_commands.describe(word="検索文字列") 67 | @commands.cooldown(1, 10, type=commands.BucketType.user) 68 | @app_commands.checks.cooldown(1, 10) 69 | @ut.runnable_check() 70 | @ut.runnable_check_for_appcmd() 71 | async def wikipedia(self, ctx, *, word:str): 72 | try: 73 | async with ctx.message.channel.typing(): 74 | wd = word 75 | sw = wikipedia.search(wd, results=1) 76 | sw1 = sw[0].replace(" ", "_") 77 | sr = wikipedia.page(sw1) 78 | embed = discord.Embed( 79 | title=sw1, description=sr.summary, color=self.bot.ec) 80 | embed.add_field(name=await ctx._("jwp-seemore"), 81 | value=f"https://ja.wikipedia.org/wiki/{sw1}") 82 | try: 83 | embed.set_image(url=sr.images[0]) 84 | except: 85 | pass 86 | await ctx.send(embed=embed) 87 | except: 88 | try: 89 | async with ctx.message.channel.typing(): 90 | if not sw is None: 91 | await ctx.send(await ctx._("jwp-found", wd, sw1)) 92 | except: 93 | await ctx.send(await ctx._("jwp-notfound")) 94 | 95 | # @search_commands.command(aliases=["jpwt", "天気", "今日の天気は"],description="日本の天気予報図を表示します。") 96 | # @commands.cooldown(1, 15, type=commands.BucketType.user) -> 形式変更で動かなくなっていましたよ 97 | async def weather_in_japan(self, ctx): 98 | 99 | if ctx.channel.permissions_for(ctx.guild.me).attach_files is True: 100 | try: 101 | async with ctx.message.channel.typing(): 102 | content = await self.bot.apple_util.get_as_binary("http://www.jma.go.jp/jp/yoho/images/000_telop_today.png") 103 | with open("imgs/weather.png", 'wb') as f: 104 | f.write(content) 105 | await ctx.send(file=discord.File("imgs/weather.png")) 106 | await ctx.send(await ctx._("jpwt-credit")) 107 | except: 108 | await ctx.send(await ctx._("jpwt-error")) 109 | else: 110 | try: 111 | await ctx.send(embed=discord.Embed(title=await ctx._("dhaveper"), description=await ctx._("per-sendfile"))) 112 | except: 113 | await ctx.send(f"{await ctx._('dhaveper')}\n{await ctx._('per-sendfile')}") 114 | 115 | @search_commands.command(aliases=["ニュース", "ニュースを見せて"],description="newsapi経由でニュースを表示します。") 116 | @commands.cooldown(1, 15, type=commands.BucketType.user) 117 | @app_commands.checks.cooldown(1, 15) 118 | @ut.runnable_check() 119 | @ut.runnable_check_for_appcmd() 120 | async def news(self, ctx): 121 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 122 | ctx.message.content) 123 | content = await self.bot.apple_util.get_as_json('https://newsapi.org/v2/top-headlines?country=jp&pagesize=5&apiKey='+self.bot.NAPI_TOKEN) 124 | embeds=[] 125 | for i in content['articles']: 126 | e=discord.Embed(title=i["title"],description=i["description"],color=self.bot.ec) 127 | e.add_field(name="もっと見る",value=i["url"]) 128 | e.set_author(name=i["author"]) 129 | e.set_footer(text=f"{i['content']}·using `https://newsapi.org/`") 130 | e.set_image(url=i["urlToImage"]) 131 | embeds.append(e) 132 | page=0 133 | msg = await ctx.send(embed=embeds[0]) 134 | await msg.add_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041)) 135 | await msg.add_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216)) 136 | while True: 137 | try: 138 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.message.author.id, timeout=30) 139 | except: 140 | break 141 | try: 142 | await msg.remove_reaction(r, u) 143 | except: 144 | pass 145 | if str(r) == str(self.bot.create_emoji_str('s_move_right',653161518170505216)): 146 | if page == 4: 147 | page = 0 148 | else: 149 | page = page + 1 150 | elif str(r) == str(self.bot.create_emoji_str("s_move_left",653161518195671041)): 151 | if page == 0: 152 | page = 4 153 | else: 154 | page = page - 1 155 | await msg.edit(embed=embeds[page]) 156 | 157 | 158 | 159 | @search_commands.command(aliases=["次の言葉でyoutube調べて"], description="YouTube検索") 160 | @app_commands.describe(word="検索文字列") 161 | @commands.cooldown(1, 10, type=commands.BucketType.user) 162 | @app_commands.checks.cooldown(1, 10) 163 | @ut.runnable_check() 164 | @ut.runnable_check_for_appcmd() 165 | async def youtube(self, ctx, *, word:str): 166 | #try: 167 | async with ctx.message.channel.typing(): 168 | with build('youtube', 'v3', developerKey=self.bot.GAPI_TOKEN) as youtube: 169 | search_response = youtube.search().list( 170 | part="id", 171 | q=word, 172 | type='video' 173 | ).execute() 174 | id = search_response['items'][0]['id']['videoId'] 175 | await ctx.send(await ctx._("youtube-found", id)) 176 | #except: 177 | #await ctx.send(await ctx._("youtube-notfound")) 178 | 179 | 180 | async def setup(bot): 181 | await bot.add_cog(search(bot)) 182 | -------------------------------------------------------------------------------- /cogs/hybrid/m10s_help.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from threading import Thread 4 | from typing import Union, Optional 5 | import discord 6 | from discord.ext import commands 7 | import asyncio 8 | 9 | import json 10 | 11 | from discord import app_commands 12 | 13 | import traceback 14 | 15 | import m10s_util as ut 16 | 17 | from my_module import dpy_interaction as dpyui 18 | 19 | 20 | 21 | class bot_help(commands.Cog): 22 | 23 | def __init__(self, bot): 24 | self.bot = bot 25 | bot.remove_command('help') 26 | 27 | @commands.hybrid_command(description="Botのヘルプを表示します。") 28 | @app_commands.describe(rcmd="詳細表示するコマンド") 29 | @commands.bot_has_permissions(embed_links=True, external_emojis=True, add_reactions=True) 30 | @app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True, add_reactions=True) 31 | @ut.runnable_check() 32 | @ut.runnable_check_for_appcmd() 33 | async def help(self, ctx, rcmd:str=None): 34 | e = discord.Embed(title="思惟奈ちゃんの使い方", description="基本的に、`/`を入力すると出てくる候補にあるコマンドのうち、思惟奈ちゃんのアイコンが表示されている物がすべてです。", color=self.bot.ec) 35 | e.add_field(name="グローバルチャットのバッジ", value="🌠:チーム☆思惟奈ちゃんメンバー\n💠:認証済みアカウント\n🔗パートナーサーバーオーナー\n🔧:グローバルチャットモデレーター\n🌟:スターユーザー(アカウント作成日制限を受けないユーザー)\n🔔:アルファテスター(一部機能の先行体験)\n🎫:有料支援者バッジ") 36 | e.add_field(name="思惟奈ちゃんサポートサーバー", value="https://discord.gg/vtn2V3v", inline=False) 37 | e.add_field(name="思惟奈ちゃんを支援する", value="https://linktr.ee/sina_chan", inline=False) 38 | await ctx.send(embed =e) 39 | """# ヘルプ内容 40 | if rcmd is None: 41 | page = 1 42 | embed = discord.Embed(title=await ctx._("help-1-t"), 43 | description=await ctx._("help-1-d"), color=self.bot.ec) 44 | embed.set_footer(text=f"page:{page}") 45 | msg = await ctx.send("> ヘルプと呼び出し方が変更になっている機能が、多くあります。\n\ 46 | それらのコマンドはスラッシュコマンド(`/`を入力することで一覧が表示されます。)での使用を強くお勧めします。\n\ 47 | サポートサーバーの質問チャンネルで、変更に関しての質問はうかがいます。\n\ 48 | 例:❌`s-level`→⭕`/level card`\ 49 |   ❌`s-play` →⭕`/music play` など", embed=embed) 50 | await msg.add_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041)) 51 | await msg.add_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216)) 52 | await msg.add_reaction("🔍") 53 | while True: 54 | try: 55 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.message.author.id, timeout=30) 56 | except: 57 | break 58 | try: 59 | await msg.remove_reaction(r, u) 60 | except: 61 | pass 62 | if str(r) == str(self.bot.create_emoji_str('s_move_right',653161518170505216)): 63 | if page == 17: 64 | page = 1 65 | else: 66 | page = page + 1 67 | embed = discord.Embed(title=await ctx._( 68 | f"help-{page}-t"), description=await ctx._(f"help-{page}-d"), color=self.bot.ec) 69 | embed.set_footer(text=f"page:{page}") 70 | await msg.edit(embed=embed) 71 | elif str(r) == str(self.bot.create_emoji_str("s_move_left",653161518195671041)): 72 | if page == 1: 73 | page = 17 74 | else: 75 | page = page - 1 76 | embed = discord.Embed(title=await ctx._( 77 | f"help-{page}-t"), description=await ctx._(f"help-{page}-d"), color=self.bot.ec) 78 | embed.set_footer(text=f"page:{page}") 79 | await msg.edit(embed=embed) 80 | elif str(r) == "🔍": 81 | await msg.remove_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041), self.bot.user) 82 | await msg.remove_reaction("🔍", self.bot.user) 83 | await msg.remove_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216), self.bot.user) 84 | qm = await ctx.send(await ctx._("help-s-send")) 85 | try: 86 | msg = await self.bot.wait_for('message', check=lambda m: m.author == ctx.author and m.channel == ctx.channel, timeout=60) 87 | sewd = msg.content 88 | except asyncio.TimeoutError: 89 | pass 90 | else: 91 | try: 92 | await msg.delete() 93 | await qm.delete() 94 | except: 95 | pass 96 | async with ctx.message.channel.typing(): 97 | lang = await ctx.user_lang() or "ja" 98 | with open(f"lang/{lang}.json", "r", encoding="utf-8") as j: 99 | f = json.load(j) 100 | sre = discord.Embed(title=await ctx._( 101 | "help-s-ret-title"), description=await ctx._("help-s-ret-desc", sewd), color=self.bot.ec) 102 | for k, v in f.items(): 103 | if k.startswith("nh-"): 104 | if sewd in k.replace("nh-", "") or sewd in str(v): 105 | sre.add_field(name=k.replace( 106 | "nh-", ""), value=f"詳細を見るには`s-help {k.replace('nh-','')}`と送信") 107 | await ctx.send(embed=sre) 108 | try: 109 | await msg.remove_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041), self.bot.user) 110 | await msg.remove_reaction("🔍", self.bot.user) 111 | await msg.remove_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216), self.bot.user) 112 | except: 113 | pass 114 | else: 115 | dcmd = await ctx._(f"nh-{str(rcmd)}") 116 | if str(dcmd) == "": 117 | await ctx.send(await ctx._("h-notfound")) 118 | else: 119 | embed = ut.getEmbed(dcmd[0], dcmd[1], self.bot.ec, *dcmd[2:]) 120 | await ctx.send(embed=embed)""" 121 | 122 | @commands.command() 123 | @ut.runnable_check() 124 | @ut.runnable_check_for_appcmd() 125 | async def help_generate(self, ctx): 126 | self.bot.tmp_helps = {} 127 | 128 | def check_c(c): 129 | if isinstance(c, commands.HybridGroup): 130 | self.bot.tmp_helps[c.qualified_name] = { 131 | "type":1, 132 | "title":f"{c.name}について", 133 | "description":c.description, 134 | "call":{ 135 | "main":f"{c.name}", 136 | "aliases":f"`{'`,`'.join(c.aliases) or 'なし'}`", 137 | "args":[f"引数:{v}" for k,v in c.clean_params.items()] 138 | }, 139 | "group":c.full_parent_name, 140 | "only_prefix":False 141 | } 142 | for sc in c.commands: 143 | check_c(sc) 144 | elif isinstance(c, commands.Group): 145 | for sc in c.commands: 146 | check_c(sc) 147 | elif isinstance(c, commands.HybridCommand): 148 | self.bot.tmp_helps[c.qualified_name]={ 149 | "type":0, 150 | "title":f"{c.name}について", 151 | "description":c.description, 152 | "call":{ 153 | "main":f"{c.name}", 154 | "aliases":f"`{'`,`'.join(c.aliases) or 'なし'}`", 155 | "args":[f"引数:{v}" for k,v in c.clean_params.items()] 156 | }, 157 | "group":c.full_parent_name, 158 | "only_prefix":False 159 | } 160 | elif isinstance(c, commands.Command): 161 | self.bot.tmp_helps[c.qualified_name]={"type":0,"title":f"{c.name}について","description":c.description,"call":{"main":f"{c.name}","aliases":f"`{'`,`'.join(c.aliases) or 'なし'}`","args":[f"引数:{v}" for k,v in c.clean_params.items()]},"group":c.full_parent_name,"only_prefix":True} 162 | 163 | for c in self.bot.commands: 164 | check_c(c) 165 | with open("nhelp.json",mode="w") as f: 166 | json.dump(self.bot.tmp_helps,f) 167 | 168 | async def setup(bot): 169 | await bot.add_cog(bot_help(bot)) -------------------------------------------------------------------------------- /cogs/m10s_manage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | import traceback 7 | import json 8 | 9 | from discord import app_commands 10 | 11 | import m10s_util as ut 12 | 13 | 14 | class manage(commands.Cog): 15 | 16 | def __init__(self, bot): 17 | self.bot = bot 18 | 19 | @commands.command() 20 | @ut.runnable_check() 21 | async def backup(self, ctx, gid: int): 22 | if not ctx.guild.me.guild_permissions.administrator: 23 | await ctx.send("このサーバーで、わたしが管理者権限を持ってないので使用できません。") 24 | return 25 | try: 26 | g = self.bot.get_guild(gid) 27 | if ctx.channel.permissions_for(ctx.author).administrator is True or ctx.author.id == 404243934210949120: 28 | pgs = await ctx.send(f"役職\n進行度:0/{len(g.roles)}") 29 | tk = 0 30 | rlid = {} 31 | async with ctx.channel.typing(): 32 | # 役職。rlid(dict)に旧id(str)で参照すれば新idが返ってくる 33 | for r in g.roles[1:][::-1]: 34 | rl = await ctx.guild.create_role(name=r.name, permissions=r.permissions, colour=r.colour, hoist=r.hoist, mentionable=r.mentionable, reason=f"{g.name}より。役職転送コマンド実行による。") 35 | await asyncio.sleep(2) 36 | rlid[str(r.id)] = rl.id 37 | tk = tk+1 38 | await pgs.edit(content=f"役職\n進行度:{tk}/{len(g.roles)}") 39 | await ctx.guild.default_role.edit(permissions=g.default_role.permissions) 40 | rlid[str(g.default_role.id)] = ctx.guild.default_role.id 41 | await pgs.edit(content=f"チャンネル\n進行度:0/{len(g.channels)}") 42 | tk = 0 43 | # チャンネル。 44 | chlt = {} 45 | for mct, mch in g.by_category(): 46 | await asyncio.sleep(2) 47 | try: 48 | ovwt = {} 49 | await asyncio.sleep(2) 50 | for k, v in mct.overwrites.items(): 51 | try: 52 | rl = ctx.guild.get_role(rlid[str(k.id)]) 53 | ovwt[rl] = v 54 | except: 55 | pass 56 | ct = await ctx.guild.create_category_channel(name=mct.name, overwrites=ovwt) 57 | chlt[str(mct.id)] = ct.id 58 | tk = tk+1 59 | await pgs.edit(content=f"チャンネル\n進行度:{tk}/{len(g.channels)}") 60 | except AttributeError: 61 | ct = None 62 | for c in mch: 63 | ovwt = {} 64 | await asyncio.sleep(2) 65 | for k, v in c.overwrites.items(): 66 | try: 67 | rl = ctx.guild.get_role(rlid[k]) 68 | ovwt[rl] = v 69 | except: 70 | pass 71 | if isinstance(c, discord.TextChannel): 72 | cch = await ctx.guild.create_text_channel(name=c.name, category=ct, topic=c.topic, slowmode_delay=c.slowmode_delay, nsfw=c.is_nsfw(), overwrites=ovwt) 73 | elif isinstance(c, discord.VoiceChannel): 74 | if ctx.guild.bitrate_limit >= c.bitrate: 75 | cch = await ctx.guild.create_voice_channel(name=c.name, category=ct, bitrate=c.bitrate, user_limit=c.user_limit, overwrites=ovwt) 76 | else: 77 | cch = await ctx.guild.create_voice_channel(name=c.name, category=ct, bitrate=ctx.guild.bitrate_limit, user_limit=c.user_limit, overwrites=ovwt) 78 | else: 79 | pass 80 | try: 81 | chlt[str(c.id)] = cch.id 82 | tk = tk+1 83 | await pgs.edit(content=f"チャンネル\n進行度:{tk}/{len(g.channels)}") 84 | except: 85 | pass 86 | await pgs.edit(content="チャンネル完了\nnext:絵文字") 87 | # 絵文字 88 | tk = 0 89 | for e in g.emojis: 90 | if len(ctx.guild.emojis) >= ctx.guild.emoji_limit: 91 | break 92 | print("looping") 93 | try: 94 | ei = await e.read() 95 | await ctx.guild.create_custom_emoji(name=e.name, image=ei) 96 | await asyncio.sleep(5) 97 | print("done") 98 | except: 99 | await ctx.send(f"```{traceback.format_exc(3)}```") 100 | await pgs.edit(content="絵文字完了\nnext:ユーザーのban状況") 101 | # ユーザーのban 102 | bm = await g.bans() 103 | tk = 0 104 | for i in bm: 105 | await g.ban(user=i.user, reason=i.reason) 106 | await asyncio.sleep(2) 107 | tk = tk+1 108 | await pgs.edit(content=f"ban状況確認\n進行度:{tk}/{len(bm)}") 109 | 110 | await pgs.edit(content="ban状況完了\nnext:サーバー設定") 111 | # サーバー設定 112 | icn = await g.icon.replace(static_format="png").read() 113 | await ctx.guild.edit(name=g.name, icon=icn, region=g.region, verification_level=g.verification_level, default_notifications=g.default_notifications, explicit_content_filter=g.explicit_content_filter) 114 | # afk 115 | if g.afk_channel: 116 | await ctx.guild.edit(afk_channel=ctx.guild.get_channel(chlt[str(g.afk_channel.id)]), afk_timeout=g.afk_timeout) 117 | # システムチャンネル 118 | if g.system_channel: 119 | await ctx.guild.edit(system_channel=ctx.guild.get_channel(chlt[str(g.system_channel.id)]), system_channel_flags=g.system_channel_flags) 120 | # サーバー招待スプラッシュ 121 | if str(g.splash_url) and ("INVITE_SPLASH" in ctx.guild.features): 122 | sp = await g.splash_url.read() 123 | await ctx.guild.edit(splash=sp) 124 | # サーバーバナー 125 | if str(g.banner_url) and ("BANNER" in ctx.guild.features): 126 | bn = await g.banner_url.read() 127 | await ctx.guild.edit(banner=bn) 128 | await ctx.send("完了しました。") 129 | else: 130 | await ctx.send("このサーバーの管理者である必要があります。") 131 | except: 132 | await ctx.send(embed=ut.getEmbed("エラー", f"詳細:```{traceback.format_exc(0)}```")) 133 | 134 | @commands.command() 135 | @ut.runnable_check() 136 | async def cemojiorole(self, ctx, name, *rlis): 137 | ig = await ctx.message.attachments[0].read() 138 | await ctx.guild.create_custom_emoji(name=name, image=ig, roles=[ctx.guild.get_role(int(i)) for i in rlis]) 139 | await ctx.send(await ctx._("created-text")) 140 | 141 | 142 | @commands.hybrid_command(aliases=["メッセージ一括削除", "次の件数分、メッセージを消して"], description="メッセージ一括削除を行います。") 143 | @app_commands.describe(msgcount="削除する件数") 144 | @commands.cooldown(1, 15, type=commands.BucketType.guild) 145 | @app_commands.checks.cooldown(1, 15, key=lambda i: (i.guild_id)) 146 | @ut.runnable_check() 147 | @ut.runnable_check_for_appcmd() 148 | async def delmsgs(self, ctx:commands.Context, msgcount:int): 149 | if ctx.channel.permissions_for(ctx.guild.me).manage_messages and (ctx.channel.permissions_for(ctx.author).manage_messages or ctx.author.id == 404243934210949120): 150 | await ctx.defer(ephemeral=True) 151 | async with ctx.message.channel.typing(): 152 | if not ctx.interaction: 153 | dmc = ctx.message 154 | await dmc.delete() 155 | dr = await ctx.channel.purge(limit=int(msgcount)) 156 | await ctx.send(await ctx._("delmsgs-del", len(dr)), ephemeral=True, delete_after=15) 157 | else: 158 | await ctx.send("> メッセージ一括削除\n あなたか私に権限がありません!", ephemeral=True) 159 | 160 | 161 | async def setup(bot): 162 | await bot.add_cog(manage(bot)) 163 | -------------------------------------------------------------------------------- /m10s_util.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- # 2 | 3 | import json 4 | import sqlite3 5 | import pickle 6 | import discord 7 | 8 | from discord.ext import commands 9 | from discord import app_commands 10 | 11 | import aiohttp 12 | 13 | #sqlite3.register_converter('pickle', pickle.loads) 14 | #sqlite3.register_converter('json', json.loads) 15 | #sqlite3.register_adapter(dict, json.dumps) 16 | #sqlite3.register_adapter(list, pickle.dumps) 17 | #db = sqlite3.connect( 18 | # "sina_datas.db", detect_types=sqlite3.PARSE_DECLTYPES, isolation_level=None) 19 | #db.row_factory = sqlite3.Row 20 | #cursor = db.cursor() 21 | 22 | # todo: zh-Hans 23 | LANG_CODE = {"ch-TW", "en", "ja"} 24 | 25 | 26 | def ondevicon(mem): 27 | tmp = "" 28 | if not str(mem.desktop_status) == "offline": 29 | tmp = tmp+"💻" 30 | if not str(mem.mobile_status) == "offline": 31 | tmp = tmp+"📱" 32 | if not str(mem.web_status) == "offline": 33 | tmp = tmp+"🌐" 34 | return tmp 35 | 36 | 37 | def getEmbed(ti, desc, color=int("0x61edff", 16), *optiontext): 38 | e = discord.Embed(title=ti, description=desc, color=color) 39 | nmb = -2 40 | while len(optiontext) >= nmb: 41 | try: 42 | nmb = nmb + 2 43 | e.add_field(name=optiontext[nmb], value=optiontext[nmb+1]) 44 | except IndexError: 45 | pass 46 | return e 47 | 48 | 49 | async def opendm(u): 50 | dc = u.dm_channel 51 | if dc is None: 52 | await u.create_dm() 53 | dc = u.dm_channel 54 | return dc 55 | 56 | 57 | async def wait_message_return(ctx, stext, sto, tout=60): 58 | await sto.send(stext) 59 | return await ctx.bot.wait_for('message', check=lambda m: m.author == ctx.author and m.channel == sto, timeout=tout) 60 | 61 | 62 | def get_vmusic(bot, member): 63 | mg = None 64 | mn = None 65 | ml = False # Default value for ml 66 | mvc = None # Initialize mvc to None 67 | for v in bot.voice_clients: 68 | vm_m = [i for i in v.channel.members if i.id == member.id] 69 | if not vm_m == []: 70 | try: 71 | mg = v.guild 72 | mn = bot.qu.get(str(v.guild.id), []) 73 | ml = bot.lp.get(str(v.guild.id), False) 74 | mvc = v 75 | break 76 | except: 77 | pass 78 | if mg and mn and mvc is not None: # Ensure mvc is not None 79 | rtn = { 80 | "name": mn[0]["video_title"], 81 | "url": mn[0]["video_url"], 82 | "uploader":mn[0]["video_up_name"], 83 | "image":mn[0]["video_thumbnail"], 84 | "guild": mg, 85 | 86 | "queue":mn, 87 | "isPlaying":mvc.is_playing(), 88 | "loop":ml, 89 | } 90 | if mvc.source: 91 | rtn["volume"] = mvc.source.volume 92 | else: 93 | rtn["volume"] = 0.5 94 | return rtn 95 | else: 96 | return None 97 | 98 | def runnable_check(): 99 | async def predicate(ctx): 100 | if isinstance(ctx, commands.Context) and hasattr(ctx.bot, "comlocks") and ctx.guild: 101 | if ctx.bot.comlocks.get(str(ctx.guild.id), []): 102 | if ctx.command.name in ctx.bot.comlocks[str(ctx.guild.id)] and not ctx.author.guild_permissions.administrator and ctx.author.id != 404243934210949120: # comlockされているかどうか 103 | return False 104 | elif isinstance(ctx, discord.Interaction) and hasattr(ctx.client, "comlocks") and ctx.guild: 105 | if ctx.client.comlocks.get(str(ctx.guild.id), []): 106 | if ctx.command.name in ctx.bot.comlocks[str(ctx.guild.id)] and not ctx.author.guild_permissions.administrator and ctx.author.id != 404243934210949120: # comlockされているかどうか 107 | return False 108 | if ctx.command.name in ctx.bot.features[0] and (not ctx.author.id in ctx.bot.team_sina): # グローバルfeatureでロックされているかどうか(メンテナンス) 109 | return False 110 | elif ctx.command.name in ctx.bot.features.get(ctx.author.id, []) or "cu:cmd" in ctx.bot.features.get(ctx.author.id, []): # featuresでのユーザーごとのコマンドロック 111 | return False 112 | elif ctx.guild and (ctx.command.name in ctx.bot.features.get(ctx.guild.id, []) or "cu:cmd" in ctx.bot.features.get(ctx.guild.id, [])): # featuresでのサーバーごとのコマンドロック 113 | return False 114 | else: 115 | return True 116 | return commands.check(predicate) 117 | 118 | def runnable_check_for_appcmd(): 119 | async def predicate(interaction: discord.Interaction): 120 | if isinstance(interaction, commands.Context) and hasattr(interaction.client, "comlocks") and interaction.guild: 121 | if interaction.client.comlocks.get(str(interaction.guild.id), []): 122 | if interaction.command.name in interaction.client.comlocks[str(interaction.guild.id)] and not interaction.user.guild_permissions.administrator and interaction.user.id != 404243934210949120: # comlockされているかどうか 123 | return False 124 | if interaction.command.name in interaction.client.features[0] and (not interaction.user.id in interaction.client.team_sina): # グローバルfeatureでロックされているかどうか(メンテナンス) 125 | return False 126 | elif interaction.command.name in interaction.client.features.get(interaction.user.id, []) or "cu:cmd" in interaction.client.features.get(interaction.user.id, []): # featuresでのユーザーごとのコマンドロック 127 | return False 128 | elif interaction.guild and (interaction.command.name in interaction.client.features.get(interaction.guild.id, []) or "cu:cmd" in interaction.client.features.get(interaction.guild.id, [])): # featuresでのサーバーごとのコマンドロック 129 | return False 130 | else: 131 | return True 132 | return app_commands.check(predicate) 133 | 134 | async def get_badges(bot, user): 135 | headers = { 136 | "User-Agent": "DiscordBot (sina-chan with discord.py)", 137 | "Authorization": f"Bot {bot.http.token}" 138 | } 139 | uid = user.id 140 | 141 | async with bot.session.get(f"https://discord.com/api/v10/users/{uid}", headers=headers) as resp: 142 | resp.raise_for_status() 143 | rq = await resp.json() 144 | return m10s_badges(rq["public_flags"]) 145 | 146 | 147 | class m10s_badges: 148 | 149 | def __init__(self, flags): 150 | 151 | self.raw_flags = flags 152 | flags = format(flags, "b") 153 | 154 | flags = flags.zfill(24)[::-1] 155 | self.staff = flags[0] == "1" 156 | self.partner = flags[1] == "1" 157 | self.hypesquad_events = flags[2] == "1" 158 | self.bug_hunter_1 = flags[3] == "1" 159 | self.hypesquad_h_bravery = flags[6] == "1" 160 | self.hypesquad_h_brilliance = flags[7] == "1" 161 | self.hypesquad_h_balance = flags[8] == "1" 162 | self.ealry_supporter = flags[9] == "1" 163 | self.team_user = flags[10] == "1" 164 | self.system = flags[12] == "1" 165 | self.bug_hunter_2 = flags[14] == "1" 166 | self.verified_bot = flags[16] == "1" 167 | self.early_verified_bot_developer = flags[17] == "1" 168 | self.discord_certified_moderator = flags[18] == "1" 169 | self.http_interaction_bot = flags[19] == "1" 170 | self.active_developer = flags[22] == "1" 171 | self.supports_commands = flags[23] == "1" 172 | 173 | self.dict_flags = { 174 | "Discord Staff": self.staff, 175 | "Partnered Server Owner": self.partner, 176 | "Hypesquad Events": self.hypesquad_events, 177 | "Bug Hunter Level 1": self.bug_hunter_1, 178 | "House Bravery": self.hypesquad_h_bravery, 179 | "House Brilliance": self.hypesquad_h_brilliance, 180 | "House Balance": self.hypesquad_h_balance, 181 | "Early Supporter": self.ealry_supporter, 182 | "Team User": self.team_user, 183 | "System": self.system, 184 | "Bug Hunter Level 2": self.bug_hunter_2, 185 | "Verified Bot": self.verified_bot, 186 | "Early Verified Bot Developer": self.early_verified_bot_developer, 187 | "Discord Certified Moderator":self.discord_certified_moderator, 188 | "Http Interaction Bot":self.http_interaction_bot, 189 | "Active Developer":self.active_developer, 190 | "Supports commands":self.supports_commands 191 | } 192 | 193 | self.n = 0 194 | 195 | def __eq__(self, arg): 196 | if isinstance(arg,m10s_badges): 197 | return self.raw_flags == arg.raw_flags 198 | else: 199 | return False 200 | 201 | def __str__(self): 202 | return ",".join(self.get_list) 203 | 204 | def __ne__(self, o: object): 205 | return not self.__eq__(o) 206 | 207 | def __hash__(self): 208 | return self.raw_flags 209 | 210 | def get_dict(self): 211 | return self.dict_flags 212 | 213 | def get_list(self): 214 | return [bn for bn, bl in self.dict_flags.items() if bl] 215 | 216 | -------------------------------------------------------------------------------- /mido_util.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | import re 5 | import random 6 | 7 | #get_status 8 | def get_status(member): 9 | if str(member.status) == "online": 10 | return "💚オンライン" 11 | elif str(member.status) == "idle": 12 | return "🧡退席中" 13 | elif str(member.status) == "dnd": 14 | return "❤取り込み中" 15 | elif str(member.status) == "offline": 16 | return "🖤オフライン" 17 | 18 | #resolve_url 19 | def resolve_url(url): 20 | HTTP_URL_REGEX = "https?://[\w/:%#\$&\?\(\)~\.=\+\-]+" 21 | URL_REGEX = "[\w/:%#\$&\?\(\)~\.=\+\-]+" 22 | 23 | if re.match(HTTP_URL_REGEX, str(url)): 24 | return str(url) 25 | elif re.match(URL_REGEX, str(url)): 26 | return f"http://" + str(url) 27 | 28 | #resolve_invite 29 | async def resolve_invite(ctx, code:str): 30 | try: 31 | return await ctx.bot.fetch_invite(code) 32 | except discord.NotFound: 33 | raise commands.BadArgument(f"Invite {code} not found.") 34 | except discord.HTTPException as e: 35 | raise commands.BadArgument(f"Error fetching invite code: {e}") 36 | 37 | #get_channel_or_user 38 | def get_channel_or_user(ctx, id:int): 39 | result = ctx.bot.get_user(id) 40 | if result is None: 41 | result = ctx.bot.get_channel(id) 42 | 43 | if result is None: 44 | raise commands.BadArgument(f"Argument {id} not found.") 45 | return result 46 | 47 | #get_features 48 | def get_features(guild): 49 | feature = guild.features 50 | 51 | key = [] 52 | 53 | features = { 54 | "VIP_REGIONS":"VIP Region", 55 | "VANITY_URL":"Vanity URL", 56 | "INVITE_SPLASH":"Splash Invite", 57 | "VERIFIED":"Verified", 58 | "PARTNERED":"Partnered", 59 | "MORE_EMOJI":"More Emoji", 60 | "DISCOVERABLE":"Discoverable", 61 | "FEATUREABLE":"Featureable", 62 | "COMMUNITY":"Community", 63 | "PUBLIC":"Public", 64 | "NEWS":"News", 65 | "BANNER":"Banner", 66 | "ANIMATED_ICON":"Animated Icon", 67 | "PUBLIC_DISABLED":"Public Disabled", 68 | "WELCOME_SCREEN_ENABLED":"Welcome Screen Enabled", 69 | "MEMBER_VERIFICATION_GATE_ENABLED":"Member Verification Gate Enabled", 70 | "PREVIEW_ENABLED":"Preview Enabled" 71 | } 72 | 73 | for i in range(len(feature)): 74 | try: 75 | key.append(features[str(feature)]) 76 | except KeyError: 77 | key.append(str(feature)) 78 | 79 | return key 80 | 81 | #get_region 82 | def get_region(guild): 83 | region = guild.region 84 | 85 | regions = { 86 | "brazil":"🇧🇷 Brazil", 87 | "europe":"🇪🇺 Europe", 88 | "hongkong":"🇭🇰 HongKong", 89 | "india":"🇮🇳 India", 90 | "japan":"🇯🇵 Japan", 91 | "russia":"🇷🇺 Russia", 92 | "singapore":"🇸🇬 Singapore", 93 | "southafrica":"🇿🇦 SouthAfrica", 94 | "sydney":"🇦🇺 Sydney", 95 | "us_central":"🇺🇸 US_Central", 96 | "us_east":"🇺🇸 US_East", 97 | "us_south":"🇺🇸 US_South", 98 | "us_west":"🇺🇸 US_West" 99 | } 100 | 101 | try: 102 | key = regions[str(region)] 103 | except KeyError: 104 | key = str(region) 105 | 106 | return key 107 | 108 | #Neta 109 | def is_jun50(ctx, member=None): 110 | if member is None: 111 | member = ctx.author 112 | 113 | return member.id in [579598776721735690, 449867036558884866] 114 | 115 | #get_guild_or_user 116 | async def get_guild_or_user(ctx, id): 117 | if ctx.bot.get_guild(id) is None: 118 | try: 119 | return await FetchUserConverter().convert(ctx, str(id)) 120 | except: 121 | raise commands.BadArgument(f"ID {id} not guild or user.") 122 | 123 | #choice 124 | def choice(l, c=1): 125 | r = [] 126 | 127 | for c in range(c): 128 | d = random.choice(l) 129 | 130 | r.append(d) 131 | l.remove(d) 132 | 133 | return r 134 | 135 | #VoiceChannelConverter 136 | class VoiceChannelConverter(commands.Converter): 137 | async def convert(self, ctx, argument): 138 | try: 139 | return await commands.VoiceChannelConverter().convert(ctx, argument) 140 | except: 141 | try: 142 | channel_id = int(argument, base=10) 143 | except ValueError: 144 | raise commands.BadArgument(f"VoiceChannel {argument!r} not found.") 145 | else: 146 | channel = ctx.bot.get_channel(channel_id) 147 | if channel is None: 148 | raise commands.BadArgument(f"VoiceChannel {argument!r} not found.") 149 | return channel 150 | 151 | #TextChannelConverter 152 | class TextChannelConverter(commands.Converter): 153 | async def convert(self, ctx, argument): 154 | try: 155 | return await commands.TextChannelConverter().convert(ctx, argument) 156 | except commands.BadArgument: 157 | try: 158 | channel_id = int(argument, base=10) 159 | except ValueError: 160 | raise commands.BadArgument(f"TextChannel {argument!r} not found.") 161 | else: 162 | channel = ctx.bot.get_channel(channel_id) 163 | if channel is None: 164 | raise commands.BadArgument(f"TextChannel {argument!r} not found.") 165 | return channel 166 | 167 | #ChannelConverter 168 | class ChannelConverter(commands.Converter): 169 | async def convert(self, ctx, argument): 170 | try: 171 | return await TextChannelConverter().convert(ctx, argument) 172 | except: 173 | try: 174 | return await VoiceChannelConverter().convert(ctx, argument) 175 | except: 176 | try: 177 | return await commands.CategoryChannelConverter().convert(ctx, argument) 178 | except: 179 | raise commands.ChannelNotFound(argument) 180 | 181 | #GuildConverter 182 | class GuildConverter(commands.Converter): 183 | async def convert(self, ctx, argument): 184 | guild = None 185 | if argument.isdigit(): 186 | guild = ctx.bot.get_guild(int(argument)) 187 | 188 | if guild is None: 189 | raise commands.BadArgument(f"Guild {argument} not found.") 190 | else: 191 | if guild is None: 192 | guild = discord.utils.get(ctx.bot.guilds, name=argument) 193 | 194 | if guild is None: 195 | raise commands.BadArgument(f"Guild {argument} not found.") 196 | 197 | return guild 198 | 199 | #BotUserConverter 200 | class BotUserConverter(commands.Converter): 201 | async def convert(self, ctx, argument): 202 | if not argument.isdigit(): 203 | raise commands.BadArgument("Not a valid bot user ID.") 204 | try: 205 | result = await ctx.bot.fetch_user(argument) 206 | except discord.NotFound: 207 | raise commands.BadArgument("Bot user not found (404).") 208 | except discord.HTTPException as exc: 209 | raise commands.BadArgument(f"Error fetching bot user: {exc}") 210 | else: 211 | if not result.bot: 212 | raise commands.BadArgument("This is not a bot.") 213 | return result 214 | 215 | #BannedMemberConverter 216 | class BannedMemberConverter(commands.Converter): 217 | async def convert(self, ctx, argument): 218 | if argument.isdigit(): 219 | member_id = int(argument, base=10) 220 | try: 221 | return await ctx.guild.fetch_ban(discord.Object(id=member_id)) 222 | except discord.NotFound: 223 | raise commands.BadArgument(f'User {argument} is not banned.') from None 224 | 225 | ban_list = await ctx.guild.bans() 226 | entity = discord.utils.find(lambda u: str(u.user) == argument, ban_list) 227 | 228 | if entity is None: 229 | raise commands.BadArgument(f'User {argument} is not banned.') 230 | return entity 231 | 232 | #FetchUserConverter 233 | class FetchUserConverter(commands.Converter): 234 | async def convert(self, ctx, argument): 235 | if not argument.isdigit(): 236 | return await commands.MemberConverter().convert(ctx, argument) 237 | try: 238 | return await ctx.bot.fetch_user(int(argument)) 239 | except discord.NotFound: 240 | raise commands.BadArgument(f'User {argument} not found.') from None 241 | except discord.HTTPException as exc: 242 | raise commands.BadArgument(f'Error fetching user: {exc}') from None 243 | 244 | #MemberConverter 245 | class MemberConverter(commands.Converter): 246 | async def convert(self, ctx, argument): 247 | if not argument.isdigit(): 248 | try: 249 | return await commands.MemberConverter().convert(ctx, argument) 250 | except: 251 | try: 252 | return await commands.UserConverter().convert(ctx, argument) 253 | except: 254 | raise commands.MemberNotFound(argument) 255 | else: 256 | try: 257 | return await commands.MemberConverter().convert(ctx, argument) 258 | except: 259 | try: 260 | return await commands.UserConverter().convert(ctx, argument) 261 | except: 262 | try: 263 | return await FetchUserConverter().convert(ctx, argument) 264 | except: 265 | raise commands.MemberNotFound(argument) -------------------------------------------------------------------------------- /cogs/m10s_role_panel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | import re 7 | import json 8 | 9 | from discord import app_commands 10 | 11 | import m10s_util as ut 12 | """↑様々な便利コマンド詰め合わせ 13 | ut.ondevicon(Member) 14 | オンライン状況に基づくデバイスアイコンテキストを返す。 15 | ut.getEmbed(title,description,color,(name,value)...) 16 | Embedのお手軽生成。これ使ったのがあるから消そうにも消せない。 17 | await ut.opendm(Member/User) 18 | DMチャンネルを返します。DMチャンネルが存在しないなんてことでは困らせません。 19 | await wait_message_return(ctx,質問するテキスト,←の送信先,待つ時間): 20 | 入力待ちの簡略化。タイムアウトの例外キャッチを忘れずに 21 | ut.get_vmusic(bot,member) 22 | 思惟奈ちゃんの音楽再生機能でそのメンバーがきいている曲を返します。 23 | """ 24 | 25 | 26 | class m10s_role_panel(commands.Cog): 27 | 28 | def __init__(self, bot): 29 | self.bot = bot 30 | self.e_check = self.bot.create_emoji_str('s_check',653161518103265291) 31 | 32 | @commands.hybrid_group(name="rolepanel",aliases=["paneledit"]) 33 | @app_commands.default_permissions(administrator=True) 34 | @commands.has_permissions(administrator=True) 35 | @commands.bot_has_permissions(manage_messages=True, manage_roles=True) 36 | @app_commands.checks.has_permissions(administrator=True) 37 | @app_commands.checks.bot_has_permissions(manage_messages=True, manage_roles=True) 38 | @ut.runnable_check() 39 | @ut.runnable_check_for_appcmd() 40 | async def role_panel(self, ctx:commands.Context): 41 | if ctx.invoked_subcommand is None: 42 | await ctx.send("""> 役職パネル機能 43 | `create`:このチャンネルに役職パネルを作成します。 44 | `add [(このチャンネルにある)パネルID] [絵文字] [役職が特定できるもの]`:パネルに役職を追加します。 45 | `remove [(このチャンネルにある)パネルID] [絵文字]`:パネルから役職を取り除きます。 46 | `delete [(このチャンネルにある)パネルID]`:該当パネルを削除します。 47 | """) 48 | 49 | @role_panel.command(name="create", description="新規役職パネルを発行します。") 50 | @ut.runnable_check() 51 | @ut.runnable_check_for_appcmd() 52 | async def p_create(self,ctx): 53 | m = await ctx.send("> パネル発行の確認\nこのチャンネルにパネルを発行してもよろしいですか?") 54 | await m.add_reaction(self.e_check) 55 | try: 56 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == m.id and u.id == ctx.author.id) 57 | except asyncio.TimeoutError: 58 | await m.edit(content="> パネルは発行されていません!\n時間内に応答がなかったため、作成はキャンセルされました。") 59 | else: 60 | if str(r.emoji) == self.e_check: 61 | pd={} 62 | pm = await ctx.send(embed=ut.getEmbed("思惟奈ちゃん役職パネル",f"このパネルは{ctx.author.mention}によって作成されました。",self.bot.ec,)) 63 | await self.bot.cursor.execute("INSERT INTO role_panels(id,roles) VALUES(%s,%s)", (pm.id,json.dumps(pd))) 64 | await m.edit(content=f"> パネルを発行しました!\n パネルのIDは`{pm.id}`です。編集や削除時に使用します。(紛失時はパネルのメッセージIDを取り出してください。)") 65 | else: 66 | await m.edit(content="> パネルは発行されていません!\n作成はキャンセルされました。") 67 | 68 | @role_panel.command(name="delete", description="役職パネルを削除します。") 69 | @ut.runnable_check() 70 | @ut.runnable_check_for_appcmd() 71 | async def p_delete(self, ctx, pid:str): 72 | try: 73 | pid = int(pid) 74 | except: 75 | await ctx.send("> パネルIDが数字ではありません!") 76 | return 77 | p = await self.bot.cursor.fetchone("select * from role_panels where id = %s",(pid,)) 78 | if p: 79 | 80 | m = await ctx.send("> パネル削除の確認\n該当パネルを削除してもよろしいですか?") 81 | await m.add_reaction(self.e_check) 82 | try: 83 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == m.id and u.id == ctx.author.id) 84 | except asyncio.TimeoutError: 85 | await m.edit(content="> パネルは削除されていません!\n時間内に応答がなかったため、削除はキャンセルされました。") 86 | else: 87 | if str(r.emoji) == self.e_check: 88 | await self.bot.cursor.execute("DELETE FROM role_panels WHERE id = %s", (pid,)) 89 | try: 90 | msg = await ctx.channel.fetch_mesasge(pid) 91 | await msg.delete() 92 | except: 93 | pass 94 | await m.edit(content="> 役職パネル\n 削除が完了しました。") 95 | else: 96 | await m.edit(content="> パネルは削除されていません!\n削除はキャンセルされました。") 97 | 98 | 99 | @role_panel.command(name="add", description="パネルに役職を追加します。") 100 | @app_commands.describe(pid="役職パネルのID") 101 | @app_commands.describe(emoji="付与するときに使う絵文字") 102 | @app_commands.describe(role="付与する役職") 103 | @ut.runnable_check() 104 | @ut.runnable_check_for_appcmd() 105 | async def p_add(self,ctx,pid:str,emoji:str,role:discord.Role): 106 | try: 107 | pid = int(pid) 108 | pmsg = await ctx.channel.fetch_message(pid) 109 | except: 110 | await ctx.send("> パネルIDが数字ではない、またはパネルがこのチャンネルに見つかりません。") 111 | return 112 | pd = await self.bot.cursor.fetchone("select * from role_panels where id = %s",(pid,)) 113 | #pd = await self.bot.cursor.fetchone() 114 | if pd: 115 | pj = json.loads(pd["roles"]) 116 | if pj.get(str(emoji),None) is None: 117 | pj[str(emoji)] = role.id 118 | try: 119 | await self.bot.cursor.execute("UPDATE role_panels SET roles = %s WHERE id = %s", (json.dumps(pj), pid)) 120 | except:await ctx.send("DB!") 121 | e:discord.Embed = pmsg.embeds[0] 122 | e.add_field(name=str(emoji), value=role.mention) 123 | e.set_footer(text=f"最終更新者:{ctx.author}") 124 | await pmsg.edit(embed=e) 125 | 126 | await pmsg.clear_reactions() 127 | 128 | for i in pj.keys(): 129 | try: 130 | await pmsg.add_reaction(i) 131 | except Exception as e: 132 | try: 133 | eid = re.match( 134 | "<:[a-zA-Z0-9_-]+:([0-9]+)>", i).group(1) 135 | ej = self.bot.get_emoji(int(eid)) 136 | await pmsg.add_reaction(ej) 137 | except: 138 | await ctx.send("付与できていないリアクションがあります。該当の役職はリアクションでの付与ができません。") 139 | 140 | else: 141 | await ctx.send(">役職パネル\n 該当絵文字を使用した役職付与が既に存在します!") 142 | else: 143 | await ctx.send("> 役職パネル\n 該当IDのパネルがありません!") 144 | 145 | @role_panel.command(name="remove",description="パネルから役職を取り除きます。") 146 | @app_commands.describe(pid="役職パネルのID") 147 | @app_commands.describe(emoji="取り除きたい役職に紐づいている絵文字") 148 | @ut.runnable_check() 149 | @ut.runnable_check_for_appcmd() 150 | async def p_remove(self,ctx,pid:str,emoji:str): 151 | try: 152 | pid = int(pid) 153 | pmsg = await ctx.channel.fetch_message(pid) 154 | except: 155 | await ctx.send("> パネルIDが数字ではない、またはパネルがこのチャンネルに見つかりません。") 156 | return 157 | pd = await self.bot.cursor.fetchone("select * from role_panels where id = %s",(pid,)) 158 | #pd = await self.bot.cursor.fetchone() 159 | if pd: 160 | pj = json.loads(pd["roles"]) 161 | if pj.get(str(emoji),None): 162 | del pj[str(emoji)] 163 | await self.bot.cursor.execute("UPDATE role_panels SET roles = %s WHERE id = %s", (json.dumps(pj), pid)) 164 | e:discord.Embed = pmsg.embeds[0] 165 | e.clear_fields() 166 | for k, v in pj.items(): 167 | rl = ctx.guild.get_role(int(v)) 168 | e.add_field(name=str(k), value=getattr(rl,"mention", "(ロール取得失敗)")) 169 | 170 | e.set_footer(text=f"最終更新者:{ctx.author}") 171 | await pmsg.edit(embed=e) 172 | 173 | await pmsg.clear_reactions() 174 | 175 | for i in pj.keys(): 176 | try: 177 | await pmsg.add_reaction(i) 178 | except Exception as e: 179 | try: 180 | eid = re.match( 181 | "<:[a-zA-Z0-9_-]+:([0-9]+)>", i).group(1) 182 | ej = self.bot.get_emoji(int(eid)) 183 | await pmsg.add_reaction(ej) 184 | except: 185 | await ctx.send("付与できていないリアクションがあります。該当の役職はリアクションでの付与ができません。") 186 | 187 | else: 188 | await ctx.send(">役職パネル\n 該当絵文字を使用した役職付与が存在しません!") 189 | else: 190 | await ctx.send("> 役職パネル\n 該当IDのパネルがありません!") 191 | 192 | 193 | 194 | @commands.Cog.listener() 195 | async def on_raw_reaction_add(self,pr): 196 | 197 | rs = await self.bot.cursor.fetchone("select roles from role_panels where id = %s",(pr.message_id,)) 198 | #rs = await self.bot.cursor.fetchone() 199 | if rs: 200 | rid = json.loads(rs["roles"]).get(str(pr.emoji),None) 201 | if rid: 202 | g = self.bot.get_guild(pr.guild_id) 203 | ch = g.get_channel(pr.channel_id) 204 | m = await ch.fetch_message(pr.message_id) 205 | rl = g.get_role(int(rid)) 206 | member = g.get_member(pr.user_id) 207 | if member.id == self.bot.user.id: 208 | return 209 | await m.remove_reaction(pr.emoji,member) 210 | try: 211 | if int(rid) in [i.id for i in member.roles]: 212 | await member.remove_roles(rl) 213 | await ch.send(f"> {member.mention}!役職を除去しました!",delete_after=5) 214 | else: 215 | await member.add_roles(rl) 216 | await ch.send(f"> {member.mention}!役職を付与しました!",delete_after=5) 217 | except: 218 | await ch.send(f"> {str(pr.emoji)}での役職の付与に失敗しました。原因を見つけてパネルを修正してください。",) 219 | 220 | 221 | 222 | async def setup(bot): 223 | await bot.add_cog(m10s_role_panel(bot)) -------------------------------------------------------------------------------- /cogs/m10s_games.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import random 6 | import time 7 | from typing import Optional 8 | from discord import app_commands 9 | import asyncio 10 | 11 | import m10s_util as ut 12 | 13 | 14 | class games(commands.Cog): 15 | 16 | def __init__(self, bot): 17 | self.bot = bot 18 | 19 | @commands.hybrid_group(description="ゲーム関連コマンド") 20 | @ut.runnable_check() 21 | @ut.runnable_check_for_appcmd() 22 | async def game(self, ctx):pass 23 | 24 | @game.command(name="hit_target", description="1から100までの数当てゲームです。") 25 | @ut.runnable_check() 26 | @ut.runnable_check_for_appcmd() 27 | async def game2(self, ctx): 28 | answer = random.randint(1, 100) 29 | await ctx.send(await ctx._("game2-ready")) 30 | i = 0 31 | while True: 32 | try: 33 | msg = await self.bot.wait_for('message', check=lambda m: m.author == ctx.author and m.channel == ctx.channel, timeout=60) 34 | except: 35 | await ctx.send(await ctx._("game2-timeout", answer)) 36 | return 37 | try: 38 | i = i + 1 39 | ur = int(msg.content) 40 | except: 41 | await ctx.send(f"{ctx.author.mention}\n{await ctx._('game2-notint')}") 42 | continue 43 | if ur > answer: 44 | await ctx.send(f'{ctx.author.mention}\n{await ctx._("game2-high")}') 45 | elif ur < answer: 46 | await ctx.send(f'{ctx.author.mention}\n{await ctx._("game2-low")}') 47 | else: 48 | await ctx.send(f'{ctx.author.mention}\n{await ctx._("game2-clear", i)}') 49 | break 50 | 51 | @game.command(name="near21", description="ブラックジャック風ゲームです。21に近づけた方の勝ちです。1or2人プレイ") 52 | @commands.bot_has_permissions(manage_messages=True, embed_links=True) 53 | @app_commands.checks.bot_has_permissions(manage_messages=True, embed_links=True) 54 | @app_commands.describe(user2="2player-gameを遊ぶ相手(自分自身を選ぶことで誰でも参加可能な形で募集)") 55 | @ut.runnable_check() 56 | @ut.runnable_check_for_appcmd() 57 | async def game1(self, ctx, user2: Optional[discord.Member]): 58 | 59 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 60 | ctx.message.content) 61 | if ctx.channel.permissions_for(ctx.guild.me).manage_messages is True: 62 | if user2 is None: 63 | embed = discord.Embed(title="game1", description=await ctx._( 64 | "game1-dis"), color=self.bot.ec) 65 | embed.add_field(name=await ctx._("game1-guide1"), 66 | value=await ctx._("game1-guide2")) 67 | embed.add_field(name=await ctx._("game1-now"), value="0") 68 | guide = await ctx.send(embed=embed) 69 | g1 = await guide.add_reaction(self.bot.create_emoji_str("s_card_d",653161517927366658)) 70 | g2 = await guide.add_reaction(self.bot.create_emoji_str('s_card_pass',653161518334214144)) 71 | uint = 0 72 | tmint = 0 73 | while(True): 74 | 75 | reaction, user = await self.bot.wait_for("reaction_add", check=lambda r, u: str(r.emoji) in [str(self.bot.create_emoji_str("s_card_d",653161517927366658)), str(self.bot.create_emoji_str('s_card_pass',653161518334214144))] and r.message.id == guide.id and u == ctx.message.author) 76 | 77 | await guide.remove_reaction(reaction, user) 78 | if str(reaction.emoji) == str(self.bot.create_emoji_str("s_card_d",653161517927366658)): 79 | dr = random.randint(1, 11) 80 | uint = uint + dr 81 | embed = discord.Embed(title="game1", description=await ctx._( 82 | "game1-dis"), color=self.bot.ec) 83 | embed.add_field(name=await ctx._("game1-guide1"), 84 | value=await ctx._("game1-guide2"), inline=False) 85 | embed.add_field(name=await ctx._("game1-now"), 86 | value=str(uint)+"(+"+str(dr)+")") 87 | if tmint < random.randint(10, 21): 88 | tmdr = random.randint(1, 11) 89 | tmint = tmint + tmdr 90 | embed.add_field(name=await ctx._( 91 | "game1-cpun"), value=await ctx._("game1-cpud")) 92 | else: 93 | embed.add_field(name=await ctx._( 94 | "game1-cpun"), value=await ctx._("game1-cpup")) 95 | await guide.edit(embed=embed) 96 | elif str(reaction.emoji) == str(self.bot.create_emoji_str('s_card_pass',653161518334214144)): 97 | break 98 | else: 99 | await ctx.send(await ctx._("game1-notr")) 100 | tmfin = 21 - tmint 101 | ufin = 21 - uint 102 | u = str(uint) 103 | sn = str(tmint) 104 | if 21 >= uint and tmfin > ufin or 21 < tmint and 21 >= uint: 105 | win = await ctx._("game1-yourwin") 106 | elif 21 >= tmint and tmfin < ufin or 21 < uint and 21 >= tmint: 107 | win = await ctx._("game1-sinawin") 108 | else: 109 | win = await ctx._("game1-dr") 110 | embed = discord.Embed(title=await ctx._( 111 | "game1-fin1", win), description=await ctx._("game1-fin2", u, sn), color=self.bot.ec) 112 | await guide.edit(embed=embed) 113 | else: 114 | if user2.bot: 115 | await ctx.send(await ctx._("game1-vsbot")) 116 | return 117 | if user2 == ctx.author: 118 | join = await ctx.send(await ctx._("game1-join-anyone")) 119 | await join.add_reaction(self.bot.create_emoji_str("s_gp_join",653161519206629386)) 120 | await join.add_reaction(self.bot.create_emoji_str('s_gp_not',653161518833074178)) 121 | try: 122 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: str(r.emoji) in [str(self.bot.create_emoji_str("s_gp_join",653161519206629386)), str(self.bot.create_emoji_str('s_gp_not',653161518833074178))] and r.message.id == join.id and u.bot is False, timeout=60) 123 | except: 124 | await ctx.send(await ctx._("game1-timeouted")) 125 | return 126 | else: 127 | join = await ctx.send(await ctx._("game1-join", user2.mention)) 128 | await join.add_reaction(self.bot.create_emoji_str("s_gp_join",653161519206629386)) 129 | await join.add_reaction(self.bot.create_emoji_str('s_gp_not',653161518833074178)) 130 | try: 131 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: str(r.emoji) in [str(self.bot.create_emoji_str("s_gp_join",653161519206629386)), str(self.bot.create_emoji_str('s_gp_not',653161518833074178))] and r.message.id == join.id and u == user2, timeout=60) 132 | except: 133 | await ctx.send(await ctx._("game1-timeouted")) 134 | return 135 | if str(r.emoji) == str(self.bot.create_emoji_str("s_gp_join",653161519206629386)): 136 | u1 = ctx.message.author 137 | u1_dm = await ut.opendm(u1) 138 | u1_card = 0 139 | u1_pass = False 140 | u2 = u 141 | u2_dm = await ut.opendm(u2) 142 | u2_card = 0 143 | u2_pass = False 144 | e1 = discord.Embed(title=await ctx._( 145 | "game1-vs-et"), description=await ctx._("game1-vs-ed", str(u1), str(u2)), color=self.bot.ec) 146 | e2 = discord.Embed(title=await ctx.l10n(u2, "game1-vs-et"), description=await ctx.l10n( 147 | u2, "game1-vs-ed", str(u1), str(u2)), color=self.bot.ec) 148 | await u1_dm.send(embed=e1) 149 | await u2_dm.send(embed=e2) 150 | while not(u1_pass and u2_pass): 151 | u1_pass = False 152 | u2_pass = False 153 | u1_msg = await u1_dm.send(await ctx._("game1-vs-yourturn", u1_card)) 154 | await u1_msg.add_reaction(self.bot.create_emoji_str("s_card_d",653161517927366658)) 155 | await u1_msg.add_reaction(self.bot.create_emoji_str('s_card_pass',653161518334214144)) 156 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: str(r.emoji) in [str(self.bot.create_emoji_str("s_card_d",653161517927366658)), str(self.bot.create_emoji_str('s_card_pass',653161518334214144))] and r.message.id == u1_msg.id and u == u1) 157 | if str(r.emoji) == str(self.bot.create_emoji_str("s_card_d",653161517927366658)): 158 | u1_card = u1_card + random.randint(1, 11) 159 | await u1_msg.edit(content=await ctx._("game1-vs-dr", u1_card)) 160 | elif str(r.emoji) == str(self.bot.create_emoji_str('s_card_pass',653161518334214144)): 161 | u1_pass = True 162 | await u1_msg.edit(content=await ctx._("game1-vs-pass", u1_card)) 163 | u2_msg = await u2_dm.send(await ctx._("game1-vs-yourturn", u2_card)) 164 | await u2_msg.add_reaction(self.bot.create_emoji_str("s_card_d",653161517927366658)) 165 | await u2_msg.add_reaction(str(self.bot.create_emoji_str('s_card_pass',653161518334214144))) 166 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: str(r.emoji) in [str(self.bot.create_emoji_str("s_card_d",653161517927366658)), str(self.bot.create_emoji_str('s_card_pass',653161518334214144))] and r.message.id == u2_msg.id and u == u2) 167 | if str(r.emoji) == str(self.bot.create_emoji_str("s_card_d",653161517927366658)): 168 | u2_card = u2_card + random.randint(1, 11) 169 | await u2_msg.edit(content=await ctx._("game1-vs-dr", u2_card)) 170 | elif str(r.emoji) == str(self.bot.create_emoji_str('s_card_pass',653161518334214144)): 171 | u2_pass = True 172 | await u2_msg.edit(content=await ctx._("game1-vs-pass", u2_card)) 173 | u1_fin = 21 - u1_card 174 | u2_fin = 21 - u2_card 175 | if 21 >= u1_card and u2_fin > u1_fin or 21 < u2_card and 21 >= u1_card: 176 | await ctx.send(await ctx._("game1-vs-fin-win", u1.mention)) 177 | elif 21 >= u2_card and u2_fin < u1_fin or 21 < u1_card and 21 >= u2_card: 178 | await ctx.send(await ctx._("game1-vs-fin-win", u2.mention)) 179 | else: 180 | await ctx.send(await ctx._("game1-vs-fin-draw")) 181 | await ctx.send(await ctx._("game1-vs-res", u1.mention, u1_card, u2.mention, u2_card)) 182 | else: 183 | await ctx.send(await ctx._("game1-cancel", ctx.author.mention)) 184 | else: 185 | try: 186 | await ctx.send(embed=discord.Embed(title=await ctx._("dhaveper"), description=await ctx._("per-manamsg"))) 187 | except: 188 | await ctx.send(f'{await ctx._("dhaveper")}\n{await ctx._("per-manamsg")}') 189 | 190 | @game.command(name="fish",description="魚釣りです。") 191 | @commands.cooldown(1, 5, type=commands.BucketType.user) 192 | @app_commands.checks.cooldown(1, 5) 193 | @ut.runnable_check() 194 | @ut.runnable_check_for_appcmd() 195 | async def fishany(self, ctx: commands.Context): 196 | lt = ["🦑", "🦐", "🐙", "🦀", "🐡", "🐠", "🐟"] + \ 197 | [i.id for i in ctx.guild.emojis] 198 | fs = random.choice(lt) 199 | if str(type(fs)) == "": 200 | fs = str(self.bot.get_emoji(fs)) 201 | gp = random.randint(1, 3) 202 | upf = await self.bot.cursor.fetchone( 203 | "select * from users where id=%s", (ctx.author.id,)) 204 | await self.bot.cursor.execute( 205 | "UPDATE users SET gpoint = %s WHERE id = %s", (upf["gpoint"]+gp, ctx.author.id)) 206 | await ctx.send(embed=ut.getEmbed("fish", await ctx._("fish-get", fs, gp)), ephemeral=True) 207 | 208 | 209 | async def setup(bot): 210 | await bot.add_cog(games(bot)) 211 | -------------------------------------------------------------------------------- /cogs/m10s_other.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Optional 4 | import discord 5 | from discord.ext import commands 6 | import random 7 | import time 8 | import asyncio 9 | import platform 10 | import re 11 | import psutil 12 | import json 13 | 14 | from discord import app_commands 15 | 16 | import m10s_util as ut 17 | 18 | 19 | class other(commands.Cog): 20 | 21 | def __init__(self, bot): 22 | self.bot = bot 23 | 24 | @commands.hybrid_command(name="support_server", description="思惟奈ちゃんサポートサーバーのURLを表示します。") 25 | @ut.runnable_check() 26 | @ut.runnable_check_for_appcmd() 27 | async def sinaguild(self, ctx): 28 | await ctx.send("サポートサーバー → https://discord.gg/vtn2V3v") 29 | 30 | @commands.command(aliases=["r", "返信", "引用"]) 31 | @ut.runnable_check() 32 | @ut.runnable_check_for_appcmd() 33 | async def reply(self, ctx, id: int, *, text): 34 | 35 | m = await ctx.channel.fetch_message(id) 36 | e = discord.Embed(description=text, color=self.bot.ec) 37 | e.add_field(name=f"引用投稿(引用された投稿の送信者:{m.author.display_name})", 38 | value=f"{m.content}\n[{self.bot.create_emoji_str('s_link_jump',653161518451392512)} この投稿に飛ぶ]({m.jump_url})") 39 | e.set_author(name=ctx.author.display_name, 40 | icon_url=ctx.author.display_avatar.replace(static_format='png')) 41 | await ctx.send(embed=e) 42 | await ctx.message.delete() 43 | 44 | 45 | @commands.command(aliases=["アンケート", "次のアンケートを開いて"]) 46 | @ut.runnable_check() 47 | async def q(self, ctx, title=None, *ctt): 48 | 49 | if title is None or ctt == []: 50 | await ctx.send(await ctx._("q-not")) 51 | else: 52 | ky = None 53 | dct = {} 54 | for tmp in ctt: 55 | if ky is None: 56 | ky = tmp 57 | else: 58 | dct[ky] = tmp 59 | ky = None 60 | itm = "" 61 | for k, v in dct.items(): 62 | if itm == "": 63 | itm = f"{k}:{v}" 64 | else: 65 | itm = itm + f"\n{k}:{v}" 66 | embed = discord.Embed(title=title, description=itm) 67 | qes = await ctx.send(embed=embed) 68 | 69 | for k in ctt[::2]: 70 | try: 71 | await qes.add_reaction(k) 72 | except Exception as e: 73 | try: 74 | eid = re.match( 75 | "<:[a-zA-Z0-9_-]+:([0-9]+)>", k).group(1) 76 | ej = self.bot.get_emoji(int(eid)) 77 | await qes.add_reaction(ej) 78 | except: 79 | await qes.delete() 80 | await ctx.send(await ctx._("q-error")) 81 | 82 | @commands.command(aliases=["クレジット", "クレジットを見せて"]) 83 | @ut.runnable_check() 84 | @ut.runnable_check_for_appcmd() 85 | async def credit(self, ctx): 86 | await ctx.send(await ctx._("credit")) 87 | 88 | @commands.command() 89 | @ut.runnable_check() 90 | async def allonline(self, ctx, mus=None): 91 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 92 | ctx.message.content) 93 | if mus is None: 94 | info = ctx.message.author 95 | else: 96 | if ctx.message.mentions: 97 | info = ctx.message.mentions[0] 98 | else: 99 | info = ctx.guild.get_member(int(mus)) 100 | if not await self.bot.can_use_online(info): 101 | return await ctx.say("cannot-send-online") 102 | if not self.bot.shares_guild(info.id, ctx.author.id): 103 | return await ctx.say("cannot-send-online") 104 | await ctx.send(f"Status:{str(info.status)}(PC:{str(info.desktop_status)},Mobile:{str(info.mobile_status)},Web:{str(info.web_status)})") 105 | 106 | @commands.hybrid_command(aliases=["ステータス", "あなたの情報を教えて"], description="思惟奈ちゃんについての色々を表示します。") 107 | @ut.runnable_check() 108 | @ut.runnable_check_for_appcmd() 109 | @app_commands.allowed_contexts(guilds=True, dms=True, private_channels=True) 110 | @app_commands.allowed_installs(guilds=True, users=True) 111 | async def botinfo(self, ctx): 112 | mem = psutil.virtual_memory() 113 | allmem = str(mem.total/1000000000)[0:3] 114 | used = str(mem.used/1000000000)[0:3] 115 | ava = str(mem.available/1000000000)[0:3] 116 | memparcent = mem.percent 117 | embed = discord.Embed(title=await ctx._( 118 | "status-inserver"), description=f"{len(self.bot.guilds)}", color=self.bot.ec) 119 | embed.add_field(name=await ctx._("status-prefix"), value="s-") 120 | embed.add_field(name=await ctx._("status-starttime"), value=self.bot.StartTime.strftime( 121 | '%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')) 122 | embed.add_field(name=await ctx._("status-ver"), 123 | value=platform.python_version()) 124 | embed.add_field(name=await ctx._("status-pros"), value=platform.processor()) 125 | embed.add_field(name=await ctx._( 126 | "status-os"), value=f"{platform.system()} {platform.release()}({platform.version()})") 127 | embed.add_field( 128 | name="メモリ", value=f"全てのメモリ容量:{allmem}GB\n使用量:{used}GB({memparcent}%)\n空き容量{ava}GB({100-memparcent}%)") 129 | embed.add_field(name="シャード内ユーザー数", value=len(self.bot.users)) 130 | embed.add_field(name="シャード内チャンネル数", value=len( 131 | [i for i in self.bot.get_all_channels()])) 132 | embed.add_field(name="シャードステータス", value=f"このサーバーのシャード番号:{self.bot.shard_id}(全{self.bot.shard_count}個)") 133 | await ctx.send(embed=embed) 134 | 135 | @commands.hybrid_command(name="return_text", aliases=["rt"], description="オウム返しします。") 136 | @app_commands.describe(te="オウム返しするテキスト") 137 | @commands.cooldown(1, 5, type=commands.BucketType.user) 138 | @app_commands.checks.cooldown(1, 5) 139 | @ut.runnable_check() 140 | @ut.runnable_check_for_appcmd() 141 | async def rettext(self, ctx: commands.Context, *, te): 142 | e = discord.Embed(color=self.bot.ec) 143 | e.set_footer(text=f"requested by {ctx.author.nick or ctx.author}({ctx.author.id})", icon_url=ctx.author.display_avatar.replace(static_format="png").url) 144 | sanitized_text = discord.utils.escape_mentions(te) 145 | if ctx.interaction: 146 | await ctx.send(sanitized_text, allowed_mentions=discord.AllowedMentions.none()) 147 | else: 148 | await ctx.send(sanitized_text, embed=e, allowed_mentions=discord.AllowedMentions.none()) 149 | await ctx.message.delete() 150 | 151 | @commands.hybrid_command(name="emoji_reaction", description="絵文字に応じたリアクションをとります。(一部絵文字のみ対応。存在しないものは運営に自動送信されて、いつか増えます。)") 152 | @app_commands.describe(emoji="絵文字") 153 | @commands.cooldown(1, 5, type=commands.BucketType.user) 154 | @app_commands.checks.cooldown(1, 5) 155 | @ut.runnable_check() 156 | @ut.runnable_check_for_appcmd() 157 | async def eatit(self, ctx, emoji: str): 158 | it = emoji.strip() 159 | if await ctx.user_lang() == "ja": 160 | response = await ctx._(f"er-{it}") 161 | if not response: 162 | await ctx.send(await ctx._("er-?")) 163 | log_channel = await self.bot.fetch_channel(993565802030698627) 164 | await log_channel.send(f"> 思惟奈のわからない絵文字があったよ。`{discord.utils.escape_markdown(emoji)}`") 165 | else: 166 | await ctx.send(response) 167 | else: 168 | await ctx.send(await ctx._("cannot-run")) 169 | 170 | @commands.command(name="randomint", aliases=["randint", "乱数", "次の条件で乱数を作って"]) 171 | @ut.runnable_check() 172 | async def randomint(self, ctx, *args): 173 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 174 | ctx.message.content) 175 | try: 176 | if len(args) == 1: 177 | s, e, c = 1, 6, int(args[0]) 178 | elif len(args) == 2: 179 | s, e, c = int(args[0]), int(args[1]), 1 180 | elif len(args) == 3: 181 | s, e, c = int(args[0]), int(args[1]), int(args[2]) 182 | else: 183 | await ctx.send(await ctx._("randomint-arg-error")) 184 | return 185 | 186 | if c >= MAX_RANDOM_COUNT: 187 | c = MAX_RANDOM_COUNT - 1 # 制限を設定 188 | intcount = [] 189 | rnd = 0 190 | for _ in range(c): 191 | tmp = random.randint(min(s, e), max(s, e)) 192 | intcount.append(tmp) 193 | rnd += tmp 194 | 195 | await ctx.send(await ctx._("randomint-return1", str(s), str(e), str(c), str(rnd), str(intcount))) 196 | except ValueError: 197 | await ctx.send(await ctx._("randomint-arg-error")) 198 | except Exception as e: 199 | print(f"Error in randomint: {e}") 200 | await ctx.send(await ctx._("randomint-return2")) 201 | 202 | @commands.hybrid_command(name="fortune", aliases=["おみくじ", "今日のおみくじをひく"], description="おみくじです。一日に何度でも引けます。") 203 | @ut.runnable_check() 204 | @ut.runnable_check_for_appcmd() 205 | async def fortune(self, ctx): 206 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 207 | ctx.message.content) 208 | rnd = random.randint(0, 6) 209 | await ctx.send(await ctx._("omikuzi-return", await ctx._("omikuzi-"+str(rnd)))) 210 | 211 | @commands.hybrid_command(description="簡易メモ機能。音楽機能のプレイリストも兼ねています。(各行に1URLでプレイリスト扱い)") 212 | @ut.runnable_check() 213 | @ut.runnable_check_for_appcmd() 214 | @discord.app_commands.choices(mode=[ 215 | discord.app_commands.Choice(name="read_a_memo", value=0), 216 | discord.app_commands.Choice(name="write", value=1), 217 | discord.app_commands.Choice(name="check_all_memo", value=2), 218 | ]) 219 | @app_commands.describe(mode="メモのモード") 220 | @app_commands.describe(memo_name="メモの名前") 221 | @app_commands.describe(memo_content="メモに書き込む内容") 222 | async def memo(self, ctx:commands.Context, mode:int, memo_name:Optional[str]="default", *, memo_content:Optional[str]): 223 | mn = memo_name 224 | ctt = memo_content 225 | mmj = await self.bot.cursor.fetchone( 226 | "select * from users where id=%s", (ctx.author.id,)) 227 | #mmj = await self.bot.cursor.fetchone() 228 | memos = json.loads(mmj["memo"]) 229 | if mode == 0: 230 | if not memos is None: 231 | if memos.get(mn) is None: 232 | await ctx.send(await ctx._("memo-r-notfound1"), ephemeral=True) 233 | else: 234 | await ctx.send(memos[mn].replace("@everyone", "everyone").replace("@here", "here"), ephemeral=True) 235 | else: 236 | await ctx.send(await ctx._("memo-r-notfound2"), ephemeral=True) 237 | elif mode == 1: 238 | if ctt is None: 239 | memos[mn] = None 240 | else: 241 | memos[mn] = ctt 242 | await self.bot.cursor.execute( 243 | "UPDATE users SET memo = %s WHERE id = %s", (json.dumps(memos), ctx.author.id)) 244 | 245 | await ctx.send(await ctx._("memo-w-write", str(mn).replace("@everyone", "everyone").replace("@here", "here")), ephemeral=True) 246 | elif mode == 2: 247 | if memos == {}: 248 | await ctx.send(await ctx._("memo-a-notfound"), ephemeral=True) 249 | else: 250 | await ctx.send(str(memos.keys()).replace("dict_keys(", await ctx._("memo-a-list")).replace(")", ""), ephemeral=True) 251 | else: 252 | await ctx.send(await ctx._("memo-except"), ephemeral=True) 253 | 254 | @commands.hybrid_command(name="create_random_group", description="ランダムなグループ分けを行えます。") 255 | @app_commands.describe(cou="1グループあたりの人数") 256 | @app_commands.describe(role="グループ分けするロール") 257 | @ut.runnable_check() 258 | @ut.runnable_check_for_appcmd() 259 | async def rg(self, ctx, cou: int, role: Optional[discord.Role]): 260 | 261 | if role is None: 262 | role = ctx.guild.default_role 263 | if cou >= 1: 264 | ml = [m.mention for m in role.members if not m.bot] 265 | ogl = [] 266 | gl = [] 267 | tmp = "hoge" 268 | while len(ml) >= cou: 269 | for i in range(cou): 270 | tmp = random.choice(ml) 271 | ogl.append(tmp) 272 | ml.remove(tmp) 273 | gl.append(ogl) 274 | ogl = [] 275 | gtxt = "\n".join([f"{'、'.join(m)}" for m in gl]) 276 | ng = ",".join(ml) 277 | await ctx.send(embed=discord.Embed(title=await ctx._("rg-title"), description=await ctx._("rg-desc", gtxt, ng), color=self.bot.ec)) 278 | else: 279 | await ctx.send(await ctx._("rg-block")) 280 | 281 | 282 | async def setup(bot): 283 | await bot.add_cog(other(bot)) -------------------------------------------------------------------------------- /cogs/m10s_owner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import discord 4 | from discord.ext import commands 5 | import asyncio 6 | from dateutil.relativedelta import relativedelta as rdelta 7 | import traceback 8 | import m10s_util as ut 9 | import textwrap 10 | 11 | import config 12 | import aiohttp 13 | 14 | from discord import app_commands 15 | 16 | class owner(commands.Cog): 17 | 18 | def __init__(self, bot): 19 | self.bot = bot 20 | 21 | @commands.command() 22 | @commands.is_owner() 23 | @ut.runnable_check() 24 | async def get_ch_id(self, ctx, cnm: str): 25 | text = [f"{str(ch)} ({ch.id})" for ch in ctx.guild.channels if cnm in str(ch)] 26 | t = "\n".join(text) 27 | await ctx.send(embed=ut.getEmbed("一致チャンネル", f"```{t}```")) 28 | 29 | #await ctx.send(embed=ut.getEmbed("一致チャンネル", str([f"{i.name}({i.id})" for i in ctx.guild.channels if i.name == cnm]))) 30 | 31 | @commands.command() 32 | @commands.is_owner() 33 | @ut.runnable_check() 34 | async def chlogs(self, ctx, cid: int, count: int): 35 | ch = self.bot.get_channel(cid) 36 | async for m in ch.history(limit=count): 37 | await ctx.author.send(embed=ut.getEmbed("メッセージ", m.clean_content, self.bot.ec, "送信者", str(m.author))) 38 | await asyncio.sleep(2) 39 | 40 | @commands.command() 41 | @commands.is_owner() 42 | @ut.runnable_check() 43 | async def dcomrun(self, ctx, cname, *, ags): 44 | c = ctx 45 | c.args = list(ags) 46 | try: 47 | await c.invoke(self.bot.get_command(cname)) 48 | except: 49 | await ctx.send(embed=discord.Embed(title="dcomrunエラー", description=traceback.format_exc(0))) 50 | 51 | @commands.command() 52 | @commands.is_owner() 53 | @ut.runnable_check() 54 | async def cu(self, ctx): 55 | await ctx.send("see you...") 56 | await self.bot.close() 57 | 58 | #check function 59 | async def is_evalable(ctx): 60 | if "allow_eval" in ctx.bot.features.get(ctx.author.id, []): 61 | return True 62 | else: 63 | return False 64 | 65 | @commands.command() 66 | @commands.check(is_evalable) 67 | @ut.runnable_check() 68 | async def sql(self, ctx, *, query): 69 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True)) 70 | try: 71 | ret = await self.bot.cursor.fetchall(query) 72 | except Exception as exc: 73 | await ctx.message.remove_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True), self.bot.user) 74 | await ctx.message.add_reaction("❌") 75 | return await ctx.author.send(embed=discord.Embed(title="sql's Error", description=f"```py\n{exc}\n```", color=self.bot.ec)) 76 | else: 77 | await ctx.message.remove_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True), self.bot.user) 78 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 79 | 80 | if ret: 81 | await ctx.send(f"```\n{ret}\n```") 82 | else: 83 | await ctx.send("```\nnone\n```") 84 | 85 | @commands.command() 86 | @commands.check(is_evalable) 87 | @ut.runnable_check() 88 | async def aev(self, ctx, *, cmd): 89 | try: 90 | await eval(cmd) 91 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 92 | except: 93 | await ctx.send(embed=discord.Embed(title="awaitEvalエラー", description=traceback.format_exc(0))) 94 | 95 | @commands.command(name="eval") 96 | @commands.check(is_evalable) 97 | @ut.runnable_check() 98 | async def eval_(self, ctx, *, cmd): 99 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True)) 100 | rt = "\n" 101 | if cmd.startswith("```py"): 102 | cmd = cmd[5:-3] 103 | elif cmd.startswith("```"): 104 | cmd = cmd[3:-3] 105 | elif cmd.startswith("```python"): 106 | cmd = cmd[9:-3] 107 | txt = f'async def evdf(ctx,bot):{rt}{rt.join([f" {i}" for i in cmd.split(rt)])}' 108 | try: 109 | exec(txt) 110 | rtn = await eval("evdf(ctx,self.bot)") 111 | await ctx.message.remove_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True), self.bot.user) 112 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 113 | if rtn: 114 | if isinstance(rtn,discord.Embed): 115 | await ctx.send(embed=rtn) 116 | else: 117 | await ctx.send(f"```{rtn}```") 118 | except: 119 | await ctx.message.remove_reaction(self.bot.create_emoji_str('s_gch_sending',653161518346534912,True), self.bot.user) 120 | await ctx.message.add_reaction("❌") 121 | await ctx.author.send(embed=discord.Embed(title="eval's Error", description=f"```{traceback.format_exc(3)}```", color=self.bot.ec)) 122 | 123 | @commands.command() 124 | @commands.is_owner() 125 | @ut.runnable_check() 126 | async def inserver(self, ctx): 127 | guilds = self.bot.guilds 128 | gcount = len(guilds)-1 129 | page = 0 130 | 131 | embed = discord.Embed( 132 | title="サーバー情報", description=f"{guilds[page].name}(id:`{guilds[page].id}`)", color=self.bot.ec) 133 | embed.add_field(name="サーバー人数", value=f"{guilds[page].member_count}人") 134 | embed.add_field( 135 | name="ブースト状態", value=f"{guilds[page].premium_tier}レベル({guilds[page].premium_subscription_count}ブースト)") 136 | embed.add_field( 137 | name="チャンネル状況", value=f"テキスト:{len(guilds[page].text_channels)}\nボイス:{len(guilds[page].voice_channels)}\nカテゴリー:{len(guilds[page].categories)}") 138 | embed.add_field( 139 | name="サーバー作成日時", value=f"{(guilds[page].created_at+ rdelta(hours=9)).strftime('%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')}") 140 | embed.add_field( 141 | name="思惟奈ちゃん導入日時", value=f"{(guilds[page].me.joined_at+ rdelta(hours=9)).strftime('%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')}") 142 | embed.add_field(name="オーナー", value=f"{guilds[page].owner}") 143 | embed.set_thumbnail(url=guilds[page].icon.replace(static_format="png").url) 144 | embed.set_footer(text=f"{page+1}/{gcount+1}") 145 | 146 | msg = await ctx.send(embed=embed) 147 | await msg.add_reaction(self.bot.create_emoji_str("s_move_left",653161518195671041)) 148 | await msg.add_reaction(self.bot.create_emoji_str('s_move_right',653161518170505216)) 149 | while True: 150 | try: 151 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.message.author.id, timeout=30) 152 | except: 153 | break 154 | try: 155 | await msg.remove_reaction(r, u) 156 | except: 157 | pass 158 | if str(r) == str(self.bot.create_emoji_str('s_move_right',653161518170505216)): 159 | if page == gcount: 160 | page = 0 161 | else: 162 | page = page + 1 163 | 164 | elif str(r) == str(self.bot.create_emoji_str("s_move_left",653161518195671041)): 165 | if page == 0: 166 | page = gcount 167 | else: 168 | page = page - 1 169 | 170 | embed = discord.Embed( 171 | title="サーバー情報", description=f"{guilds[page].name}(id:`{guilds[page].id}`)", color=self.bot.ec) 172 | embed.add_field( 173 | name="サーバー人数", value=f"{guilds[page].member_count}人") 174 | embed.add_field( 175 | name="ブースト状態", value=f"{guilds[page].premium_tier}レベル({guilds[page].premium_subscription_count}ブースト)") 176 | embed.add_field( 177 | name="チャンネル状況", value=f"テキスト:{len(guilds[page].text_channels)}\nボイス:{len(guilds[page].voice_channels)}\nカテゴリー:{len(guilds[page].categories)}") 178 | embed.add_field( 179 | name="サーバー作成日時", value=f"{(guilds[page].created_at+ rdelta(hours=9)).strftime('%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')}") 180 | embed.add_field( 181 | name="思惟奈ちゃん導入日時", value=f"{guilds[page].me.joined_at.strftime('%Y{0}%m{1}%d{2} %H{3}%M{4}%S{5}').format(*'年月日時分秒')}") 182 | embed.add_field(name="オーナー", value=f"{guilds[page].owner}") 183 | embed.set_thumbnail( 184 | url=guilds[page].icon.replace(static_format="png").url) 185 | embed.set_footer(text=f"{page+1}/{gcount+1}") 186 | await msg.edit(embed=embed) 187 | 188 | @commands.command() 189 | @commands.is_owner() 190 | @ut.runnable_check() 191 | async def dmember(self, ctx, *, mus=None): 192 | info = None 193 | tmp2 = None 194 | if mus is None: 195 | await ctx.send("メンバーid/名前の指定は必須です。") 196 | else: 197 | tmp = None 198 | try: 199 | tmp = int(mus) 200 | except: 201 | pass 202 | for guild in self.bot.guilds: 203 | if tmp: 204 | tmp2 = guild.get_member(int(mus)) 205 | else: 206 | tmp2 = guild.get_member_named(mus) 207 | if tmp2: 208 | info = tmp2 209 | break 210 | if info: 211 | async with ctx.message.channel.typing(): 212 | if ctx.guild.owner == info: 213 | embed = discord.Embed(title=await ctx._( 214 | "userinfo-name"), description=f"{info.name} - {ut.ondevicon(info)} - {await ctx._('userinfo-owner')}", color=info.color) 215 | else: 216 | embed = discord.Embed(title=await ctx._( 217 | "userinfo-name"), description=f"{info.name} - {ut.ondevicon(info)}", color=info.color) 218 | embed.add_field(name=await ctx._( 219 | "userinfo-joindiscord"), value=info.created_at) 220 | embed.add_field(name=await ctx._("userinfo-id"), value=info.id) 221 | embed.add_field(name=await ctx._("userinfo-online"), 222 | value=f"{str(info.status)}") 223 | embed.add_field(name=await ctx._("userinfo-isbot"), 224 | value=str(info.bot)) 225 | embed.add_field(name=await ctx._( 226 | "userinfo-displayname"), value=info.display_name) 227 | embed.add_field(name=await ctx._( 228 | "userinfo-joinserver"), value=info.joined_at) 229 | embed.set_footer( 230 | text=f"サーバー:{info.guild.name}({info.guild.id})") 231 | if info.activity is not None: 232 | try: 233 | embed.add_field(name=await ctx._( 234 | "userinfo-nowplaying"), value=f'{info.activity.name}') 235 | except: 236 | embed.add_field(name=await ctx._( 237 | "userinfo-nowplaying"), value=info.activity) 238 | hasroles = "" 239 | for r in info.roles: 240 | hasroles = hasroles + f"{r.mention}," 241 | embed.add_field(name=await ctx._("userinfo-roles"), value=hasroles) 242 | embed.set_thumbnail( 243 | url=info.display_avatar.replace(static_format='png')) 244 | embed.add_field(name=await ctx._("userinfo-iconurl"), 245 | value=info.display_avatar.replace(static_format='png').url) 246 | await ctx.send(embed=embed) 247 | else: 248 | await ctx.send("一致するユーザーが、共通サーバーに見つかりませんでした。") 249 | 250 | @commands.command() 251 | @ut.runnable_check() 252 | async def cuglobal(self, ctx, *cids): 253 | upf = await self.bot.cursor.fetchone( 254 | "select * from users where id=%s", (ctx.author.id,)) 255 | #upf = await self.bot.cursor.fetchone() 256 | if upf["gmod"] == 1: 257 | async with ctx.channel.typing(): 258 | for cid in [int(i) for i in cids]: 259 | await asyncio.sleep(0.5) 260 | try: 261 | await self.bot.cursor.execute("delete from gchat_cinfo where id = %s", (cid,)) 262 | except: 263 | pass 264 | await ctx.send("強制切断できてるか確認してねー") 265 | 266 | 267 | @commands.command() 268 | @commands.is_owner() 269 | @ut.runnable_check() 270 | async def guserft(self, ctx, *, nandt): 271 | lt = [f"{str(m)}({m.id})" for m in self.bot.users if nandt in str(m)] 272 | if lt: 273 | t = "\n".join(lt) 274 | await ctx.send(embed=ut.getEmbed(f"{str(nandt)}に一致するユーザー", f"```{t}```")) 275 | else: 276 | await ctx.send(embed=ut.getEmbed("", "一致ユーザーなし")) 277 | 278 | @commands.command() 279 | @commands.is_owner() 280 | @ut.runnable_check() 281 | async def guildv(self, ctx, gid: int, bl: bool=True): 282 | print(f'{ctx.message.author.name}({ctx.message.guild.name})_' + 283 | ctx.message.content) 284 | await self.bot.cursor.execute( 285 | "UPDATE guilds SET verified = %s WHERE id = %s", (bl, gid)) 286 | await ctx.send(f"サーバー`{await self.bot.fetch_guild(gid)}`の認証状態を{str(bl)}にしました。") 287 | 288 | 289 | @commands.group() 290 | @commands.is_owner() 291 | @ut.runnable_check() 292 | async def manage_features(self, ctx): 293 | pass 294 | 295 | @manage_features.command(name="view",description="他者のfeaturesを見る") 296 | @app_commands.describe(uid="ユーザーのid") 297 | @ut.runnable_check() 298 | async def view_(self,ctx,uid:int): 299 | await ctx.reply(f"```py\n{self.bot.features.get(uid,[])}```") 300 | 301 | @manage_features.command(name="del", description="feature削除") 302 | @app_commands.describe(uid="ユーザーのid") 303 | @app_commands.describe(feature="取り除くfeature") 304 | @ut.runnable_check() 305 | async def del_(self,ctx,uid:int,feature:str): 306 | uf = self.bot.features.get(uid,None) 307 | if uf and feature in uf: 308 | self.bot.features[uid].remove(feature) 309 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 310 | 311 | @manage_features.command(name="add",description="feature追加") 312 | @app_commands.describe(uid="ユーザーのid") 313 | @app_commands.describe(feature="追加するfeature") 314 | @ut.runnable_check() 315 | async def add_(self,ctx,uid:int,feature): 316 | uf = self.bot.features.get(uid,None) 317 | if uf: 318 | self.bot.features[uid].append(feature) 319 | else: 320 | self.bot.features[uid] = [feature] 321 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 322 | 323 | @manage_features.command(name="reload",description="feature再読み込み") 324 | @ut.runnable_check() 325 | async def reload_(self,ctx): 326 | import importlib 327 | import config 328 | importlib.reload(config) 329 | self.bot.features = config.sp_features 330 | await ctx.message.add_reaction(self.bot.create_emoji_str('s_check',653161518103265291)) 331 | 332 | async def setup(bot): 333 | await bot.add_cog(owner(bot)) 334 | -------------------------------------------------------------------------------- /cogs/m10s_auth_wiz.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord import app_commands 4 | import asyncio 5 | import m10s_util as ut 6 | 7 | import json 8 | 9 | 10 | class m10s_auth_wiz(commands.Cog): 11 | 12 | def __init__(self, bot): 13 | self.bot = bot 14 | 15 | @commands.Cog.listener() 16 | async def on_member_join(self, m): 17 | auths = await self.bot.cursor.fetchone( 18 | "select * from welcome_auth where id = %s", (m.guild.id,)) 19 | #auths = await self.bot.cursor.fetchone() 20 | if auths: 21 | if bool(auths["uses"]) and not(m.bot): 22 | if type(auths["next_reaction"]) is int: 23 | nr = self.bot.get_emoji(auths["next_reaction"]) 24 | else: 25 | nr = auths["next_reaction"] 26 | ow = { 27 | m: discord.PermissionOverwrite(read_messages=True, send_messages=True), 28 | m.guild.default_role: discord.PermissionOverwrite(read_messages=False), 29 | m.guild.me: discord.PermissionOverwrite( 30 | read_messages=True, send_messages=True, manage_messages=True) 31 | } 32 | for i in json.loads(auths["can_view"]): 33 | rl = m.guild.get_role(i) 34 | if rl: 35 | ow[rl] = discord.PermissionOverwrite( 36 | read_messages=True) 37 | cg = m.guild.get_channel(auths["category"]) 38 | if cg: 39 | ch = await cg.create_text_channel(f"sinaauth-{m.name}", overwrites=ow, topic=str(m.id), position=0) 40 | else: 41 | ch = await m.guild.create_text_channel(f"sinaauth-{m.name}", overwrites=ow, topic=str(m.id), position=0) 42 | msg = await ch.send("please wait...\nしばらくお待ちください…") 43 | for i in json.loads(auths["au_w"]): 44 | await msg.edit(content=None, embed=ut.getEmbed(f"サーバーユーザー認証", f"※{nr}で進行します。\n{i['text']}")) 45 | for r in i["reactions"]: 46 | if type(r) is int: 47 | rct = self.bot.get_emoji(r) 48 | else: 49 | rct = r 50 | await msg.add_reaction(rct) 51 | await msg.add_reaction(nr) 52 | r, _ = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == m.id and r.emoji == auths["next_reaction"]) 53 | ridx = [i["reactions"].index(str(r.emoji)) for r in r.message.reactions if r.count == 2 and r.emoji != nr] 54 | for ri in ridx: 55 | grl = m.guild.get_role(i["give_role"][ri]) 56 | if grl: 57 | await m.add_roles(grl) 58 | await msg.clear_reactions() 59 | await m.add_roles(m.guild.get_role(auths["give_role"])) 60 | await ch.send("> サーバーユーザー認証\n あなたの認証が完了しました!") 61 | 62 | @commands.hybrid_command(name="authsetting", aliases=["Auth","Authsettings"], description="簡易メンバー認証を作成できます。") 63 | @commands.has_permissions(administrator=True) 64 | @app_commands.default_permissions(administrator=True) 65 | @commands.bot_has_permissions(manage_messages=True,manage_roles=True) 66 | @ut.runnable_check() 67 | @ut.runnable_check_for_appcmd() 68 | async def _setting(self, ctx): 69 | auths = await self.bot.cursor.fetchone( 70 | "select * from welcome_auth where id = %s", (ctx.guild.id,)) 71 | #auths = await self.bot.cursor.fetchone() 72 | if auths: 73 | use = bool(auths["uses"]) 74 | else: 75 | use = False 76 | e = discord.Embed(title="認証ウィザードの設定", description=""" 77 | ✏で設定を行います。 78 | 🔄で利用設定を切り替えます。 79 | ❌で表示を消します。""", color=self.bot.ec) 80 | e.set_footer(text="create by mii-10") 81 | if use: 82 | e.add_field(name="利用状況", value="使用する", inline=False) 83 | roles = "\n".join([str(ctx.guild.get_role(i)) 84 | for i in json.loads(auths["can_view"])]) 85 | e.add_field(name="認証を閲覧できる役職", value=roles, inline=False) 86 | if auths["category"]: 87 | category = self.bot.get_channel(auths["category"]) 88 | e.add_field( 89 | name="作成されるカテゴリー", value=f"{category.name}({category.id})", inline=False) 90 | else: 91 | catagory = None 92 | e.add_field(name="作成されるカテゴリー", 93 | value=f"カテゴリーに所属しない", inline=False) 94 | if isinstance(auths["next_reaction"], str): 95 | nr = auths["next_reaction"] 96 | elif isinstance(auths["next_reaction"], int): 97 | nr = self.bot.get_emoji(auths["next_reaction"]) 98 | e.add_field(name="次に進むリアクション", value=nr, inline=False) 99 | auth_w = json.loads(auths["au_w"]) 100 | e.add_field(name="現在の認証の長さ", value=len(auth_w), inline=False) 101 | grole = ctx.guild.get_role(auths["give_role"]) 102 | e.add_field(name="与える役職", value=str(grole), inline=False) 103 | else: 104 | e.add_field(name="利用状況", value="使用しない", inline=False) 105 | m = await ctx.send(embed=e) 106 | await m.add_reaction("✏") 107 | await m.add_reaction("🔄") 108 | await m.add_reaction("❌") 109 | try: 110 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == m.id and u.id == ctx.author.id and r.emoji in ["✏", "🔄", "❌"]) 111 | except asyncio.TimeoutError: 112 | await m.delete() 113 | await ctx.send("一定時間リアクションがなかったため、パネルを削除しました。設定する際は再度コマンド実行をお願いします。") 114 | return 115 | if r.emoji == "✏": 116 | await m.clear_reactions() 117 | await ctx.send("設定を開始します。DMに移動してください。") 118 | if auths: # 設定がある場合の処理を行います メモ:保存処理をちゃんと書くこと! 119 | udm = await ut.opendm(ctx.author) 120 | msg = await udm.send("""> 既に設定があります。何を変更しますか? 121 | ▶:次へ進む共通の絵文字 122 | 🎫:作成するカテゴリー 123 | 📖:ウィザードの内容 124 | 🔍:閲覧できる役職の変更 125 | 🎖:与える役職 126 | """) 127 | await msg.add_reaction("▶") 128 | await msg.add_reaction("🎫") 129 | await msg.add_reaction("📖") 130 | await msg.add_reaction("🔍") 131 | await msg.add_reaction("🎖") 132 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.author.id and r.emoji in "▶🎫📖🔍🎖") 133 | if r.emoji == "🎖": 134 | m = await ut.wait_message_return(ctx, "認証後、与える役職のIDを送信してください。", udm, 60) 135 | grole = int(m.content) 136 | await self.bot.cursor.execute( 137 | "UPDATE welcome_auth SET give_role = %s WHERE id = %s", (grole, ctx.guild.id)) 138 | await udm.send("変更が完了しました。") 139 | elif r.emoji == "▶": 140 | m = await ut.wait_message_return(ctx, "作成するウィザードの進行絵文字を送ってください。\nサーバー絵文字の場合は、IDを送信してください。", udm, 60) 141 | try: 142 | nr = int(m.content) 143 | except: 144 | nr = m.content 145 | await self.bot.cursor.execute( 146 | "UPDATE welcome_auth SET next_reaction = %s WHERE id = %s", (nr, ctx.guild.id)) 147 | await udm.send("変更が完了しました。") 148 | elif r.emoji == "🎫": 149 | m = await ut.wait_message_return(ctx, "チャンネルを作成するカテゴリーのIDを送信してください。\nカテゴリーを作らない場合は、任意のテキストを送信してください。", udm, 60) 150 | try: 151 | category = int(m.content) 152 | except: 153 | category = None 154 | await self.bot.cursor.execute( 155 | "UPDATE welcome_auth SET category = %s WHERE id = %s", (category, ctx.guild.id)) 156 | await udm.send("変更が完了しました。") 157 | elif r.emoji == "📖": 158 | auths = await self.bot.cursor.fetchone( 159 | "select * from welcome_auth where id = %s", (ctx.guild.id,)) 160 | #auths = await self.bot.cursor.fetchone() 161 | if isinstance(auths["next_reaction"], str): 162 | nr = auths["next_reaction"] 163 | elif isinstance(auths["next_reaction"], int): 164 | nr = self.bot.get_emoji(auths["next_reaction"]) 165 | else: 166 | nr = "➡" 167 | 168 | seted = False 169 | auth_w = [] 170 | while not seted: 171 | msg = await udm.send("> 編集ウィザード\n注意:ページ情報は新しいものに置き換えられます。\n✏:次のページを作成\n✅:終了する") 172 | await msg.add_reaction("✏") 173 | await msg.add_reaction("✅") 174 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.author.id and r.emoji in ["✏", "✅"]) 175 | if r.emoji == "✅": 176 | if len(auth_w) == 0: 177 | await udm.send("> 作成はまだ続いています!\nウィザードは、必ず1ページは作成する必要があります!") 178 | else: 179 | seted = True 180 | elif r.emoji == "✏": 181 | tmp = {} 182 | msg = await udm.send(f"> 編集ウィザード\nこのメッセージに使いたいリアクションをした後、最後に{nr}を押してください。") 183 | await msg.add_reaction(nr) 184 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.author.id and r.emoji == nr) 185 | tmp["reactions"] = [ 186 | str(r.emoji) for r in r.message.reactions if not r.emoji == nr] 187 | tmp["give_role"] = [] 188 | for r in tmp["reactions"]: 189 | ridm = await ut.wait_message_return(ctx, f"> 編集ウィザード\n{r}で役職を付与する場合は役職のIDを、しない場合は数字ではない任意のテキストを送信してください。", udm, 60) 190 | try: 191 | rid = int(ridm.content) 192 | except: 193 | rid = None 194 | tmp["give_role"].append(rid) 195 | tmsg = await ut.wait_message_return(ctx, f"> 編集ウィザード\n最後に、そのページのテキストを送信してください。", udm, 60) 196 | tmp["text"] = tmsg.content 197 | auth_w.append(tmp) 198 | await self.bot.cursor.execute( 199 | "UPDATE welcome_auth SET au_w = %s WHERE id = %s", (json.dumps(auth_w), ctx.guild.id)) 200 | await udm.send("変更が完了しました。") 201 | elif r.emoji == "🔍": 202 | tmsg = await ut.wait_message_return(ctx, f"> 編集ウィザード\nそのチャンネルを閲覧できる役職のIDをスペース区切りで送信してください。", udm, 60) 203 | cv = [int(i) for i in tmsg.content.split(" ")] 204 | await self.bot.cursor.execute( 205 | "UPDATE welcome_auth SET can_view = %s WHERE id = %s", (json.dumps(cv), ctx.guild.id)) 206 | await udm.send("変更が完了しました。") 207 | else: # 設定が存在しない場合、初期設定を行います。 208 | udm = await ut.opendm(ctx.author) 209 | try: 210 | m = await ut.wait_message_return(ctx, "作成するウィザードの進行絵文字を送ってください。\nサーバー絵文字の場合は、IDを送信してください。", udm, 60) 211 | try: 212 | nr = int(m.content) 213 | except: 214 | nr = m.content 215 | 216 | # カテゴリ→ウィザードの内容で設定を作る 217 | 218 | tmsg = await ut.wait_message_return(ctx, f"> 作成ウィザード\nそのチャンネルを閲覧できる役職のIDをスペース区切りで送信してください。", udm, 60) 219 | cv = [int(i) for i in tmsg.content.split(" ")] 220 | 221 | m = await ut.wait_message_return(ctx, "チャンネルを作成するカテゴリーのIDを送信してください。\nカテゴリーを作らない場合は、任意のテキストを送信してください。", udm, 60) 222 | try: 223 | category = int(m.content) 224 | except: 225 | category = None 226 | 227 | m = await ut.wait_message_return(ctx, "認証後、与える役職のIDを送信してください。", udm, 60) 228 | grole = int(m.content) 229 | seted = False 230 | auth_w = [] 231 | while not seted: 232 | msg = await udm.send("> ウィザードの作成\n✏:次のページを作成\n✅:終了する") 233 | await msg.add_reaction("✏") 234 | await msg.add_reaction("✅") 235 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.author.id and r.emoji in ["✏", "✅"]) 236 | if r.emoji == "✅": 237 | if len(auth_w) == 0: 238 | await udm.send("> 作成はまだ続いています!\nウィザードは、必ず1ページは作成する必要があります!") 239 | else: 240 | seted = True 241 | elif r.emoji == "✏": 242 | tmp = {} 243 | msg = await udm.send(f"> 作成ウィザード\nこのメッセージに使いたいリアクションをした後、最後に{nr}を押してください。") 244 | await msg.add_reaction(nr) 245 | r, u = await self.bot.wait_for("reaction_add", check=lambda r, u: r.message.id == msg.id and u.id == ctx.author.id and r.emoji == nr) 246 | tmp["reactions"] = [ 247 | str(r.emoji) for r in r.message.reactions if not r.emoji == nr] 248 | tmp["give_role"] = [] 249 | for r in tmp["reactions"]: 250 | ridm = await ut.wait_message_return(ctx, f"> 作成ウィザード\n{r}で役職を付与する場合は役職のIDを、しない場合は数字ではない任意のテキストを送信してください。", udm, 60) 251 | try: 252 | rid = int(ridm.content) 253 | except: 254 | rid = None 255 | tmp["give_role"].append(rid) 256 | tmsg = await ut.wait_message_return(ctx, f"> 作成ウィザード\n最後に、そのページのテキストを送信してください。", udm, 60) 257 | tmp["text"] = tmsg.content 258 | auth_w.append(tmp) 259 | await self.bot.cursor.execute("insert into welcome_auth(id,category,uses,can_view,next_reaction,au_w,give_role) values(%s,%s,%s,%s,%s,%s,%s)", ( 260 | ctx.guild.id, category, 1, json.dumps(cv), nr, json.dumps(auth_w), grole)) 261 | await udm.send("> 作成ウィザード\n作成が完了しました!設定の確認や変更は、再度`s-Authsetting`コマンドで行えます。") 262 | except asyncio.TimeoutError: 263 | await udm.send("タイムアウトしました。再度設定をするには、初めからやり直してください。") 264 | elif r.emoji == "🔄": 265 | if auths: 266 | await m.clear_reactions() 267 | if use: 268 | await self.bot.cursor.execute( 269 | "UPDATE welcome_auth SET uses = %s WHERE id = %s", (0, ctx.guild.id)) 270 | else: 271 | await self.bot.cursor.execute( 272 | "UPDATE welcome_auth SET uses = %s WHERE id = %s", (1, ctx.guild.id)) 273 | await m.edit(embed=ut.getEmbed("認識ウィザード", f"利用設定を{not use}に切り替えました。")) 274 | else: 275 | await m.edit(embed=ut.getEmbed("認識ウィザード", f"初めに✏絵文字から利用設定を行ってください。")) 276 | elif r.emoji == "❌": 277 | await m.delete() 278 | 279 | 280 | async def setup(bot): 281 | await bot.add_cog(m10s_auth_wiz(bot)) 282 | --------------------------------------------------------------------------------