discord-py-help-lib 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 hashimotok
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.
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.2
2
+ Name: discord-py-help-lib
3
+ Version: 0.0.1
4
+ Summary: A library to assist with Discord.py development
5
+ Home-page: https://github.com/hashimotok-ecsv/discord-py-help-lib
6
+ Download-URL: https://github.com/hashimotok-ecsv/discord-py-help-lib
7
+ Author: hashimotok
8
+ Author-email: contact@hashimotok.dev
9
+ License: MIT
10
+ Project-URL: Documentation, https://discord-py-help-lib.readthedocs.io/
11
+ Project-URL: Source, https://github.com/hashimotok-ecsv/discord-py-help-lib
12
+ Project-URL: Tracker, https://github.com/hashimotok-ecsv/discord-py-help-lib/issues
13
+ Keywords: discord.py help library
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: OS Independent
21
+ Requires-Python: >=3.10.6
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: discord.py>=2.5.2
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: download-url
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: project-url
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # Auxiliary for discord.py Library
40
+
41
+ ![PyPI version](https://img.shields.io/pypi/v/discord-py-auxiliary-lib.svg)
42
+ ![Python version](https://img.shields.io/pypi/pyversions/discord-py-auxiliary-lib.svg)
43
+ ![License](https://img.shields.io/pypi/l/discord-py-auxiliary-lib.svg)
44
+
45
+ ---
46
+
47
+ ## 📦 概要
48
+
49
+ `discord-py-auxiliary-lib` は、discord.pyで役職パネルを簡単に実装するための Python ライブラリです。
50
+
51
+ 主な機能は以下のとおりです:
52
+
53
+ - 後日更新
54
+
55
+ ---
56
+
57
+ ## ✨ 特徴
58
+
59
+ - ✅ 簡単に使用が可能
60
+
61
+ ---
62
+
63
+ ## 🔧 インストール
64
+
65
+ ### PyPIからインストール:
66
+ ```bash
67
+ pip install discord-py-auxiliary-lib
68
+ ```
69
+ ### githubからインストール:
70
+ ```bash
71
+ pip install git+https://github.com/hashimotok-ecsv/discord_py_auxiliary_lib.git
72
+ ```
73
+ ## 使い方
74
+ ```python
75
+ # 後日更新
76
+ ```
77
+ #### 管理者用
78
+ ##### 更新方法
79
+ ```bash
80
+ py setup.py sdist
81
+ py setup.py bdist_wheel
82
+ py -m twine upload --repository testpypi dist/*
83
+ py -m twine upload --repository pypi dist/*
84
+ ```
@@ -0,0 +1,46 @@
1
+ # Auxiliary for discord.py Library
2
+
3
+ ![PyPI version](https://img.shields.io/pypi/v/discord-py-auxiliary-lib.svg)
4
+ ![Python version](https://img.shields.io/pypi/pyversions/discord-py-auxiliary-lib.svg)
5
+ ![License](https://img.shields.io/pypi/l/discord-py-auxiliary-lib.svg)
6
+
7
+ ---
8
+
9
+ ## 📦 概要
10
+
11
+ `discord-py-auxiliary-lib` は、discord.pyで役職パネルを簡単に実装するための Python ライブラリです。
12
+
13
+ 主な機能は以下のとおりです:
14
+
15
+ - 後日更新
16
+
17
+ ---
18
+
19
+ ## ✨ 特徴
20
+
21
+ - ✅ 簡単に使用が可能
22
+
23
+ ---
24
+
25
+ ## 🔧 インストール
26
+
27
+ ### PyPIからインストール:
28
+ ```bash
29
+ pip install discord-py-auxiliary-lib
30
+ ```
31
+ ### githubからインストール:
32
+ ```bash
33
+ pip install git+https://github.com/hashimotok-ecsv/discord_py_auxiliary_lib.git
34
+ ```
35
+ ## 使い方
36
+ ```python
37
+ # 後日更新
38
+ ```
39
+ #### 管理者用
40
+ ##### 更新方法
41
+ ```bash
42
+ py setup.py sdist
43
+ py setup.py bdist_wheel
44
+ py -m twine upload --repository testpypi dist/*
45
+ py -m twine upload --repository pypi dist/*
46
+ ```
@@ -0,0 +1,4 @@
1
+ from .select import *
2
+ from .webhook_sender import *
3
+
4
+ __version__ = '0.0.1'
@@ -0,0 +1,138 @@
1
+ import discord
2
+ import traceback
3
+ from discord.ext import commands
4
+ from typing import Callable, Optional, Awaitable
5
+
6
+ def select_category(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> dict:
7
+ category_list = guild.categories
8
+ total_categories = len(category_list)
9
+
10
+ # 1ページあたりの項目数
11
+ items_per_page = 25
12
+ # ページの範囲を計算
13
+ start = items_per_page * (page - 1)
14
+ end = items_per_page * page
15
+ last_page = (total_categories + items_per_page - 1) // items_per_page # ページ数を計算
16
+ print(f"start: {start}, end: {end}, total_categories: {total_categories}")
17
+ # 範囲外なら最後のページを選ぶ
18
+ if start >= total_categories:
19
+ start = items_per_page * (last_page - 1)
20
+ end = total_categories
21
+ page = last_page
22
+ options = []
23
+ count: int = 0
24
+ for category in category_list[start:end]:
25
+ options.append(discord.SelectOption(label="📂"+category.name, value=str(category.id)))
26
+ count += 1
27
+ if not multiselect:
28
+ count = 1
29
+ select_ui = discord.ui.Select(custom_id=select_ui_id + "_" + str(page), placeholder=placeholder, options=options, max_values=count)
30
+ return {"select_ui": select_ui, "page": page, "last_page": last_page}
31
+
32
+ def get_category_select(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> discord.ui.View:
33
+ try:
34
+ data = select_category(guild, select_ui_id, placeholder, page, multiselect)
35
+ view: discord.ui.View = discord.ui.View()
36
+ select_category_ui: discord.ui.Select = data["select_ui"]
37
+ view.add_item(select_category_ui)
38
+ prev_button: discord.ui.Button = discord.ui.Button(style=discord.ButtonStyle.red, label="前へ", custom_id=f"{select_ui_id}_back_select_{page - 1}", disabled=False if page > 1 else True)
39
+ view.add_item(prev_button)
40
+ next_button: discord.ui.Button = discord.ui.Button(style=discord.ButtonStyle.green, label="次へ", custom_id=f"{select_ui_id}_next_select_{page + 1}")
41
+ view.add_item(next_button)
42
+ cancel_button: discord.ui.Button = discord.ui.Button(style=discord.ButtonStyle.red, label="キャンセル", custom_id=f"{select_ui_id}_cancel_select")
43
+ view.add_item(cancel_button)
44
+ return view
45
+ except Exception as e:
46
+ print(f"Error in get_category_select: {e}")
47
+ traceback.print_exc()
48
+ return None
49
+
50
+ class CategorySelectHandler:
51
+ def __init__(self,
52
+ bot: commands.Bot,
53
+ custom_id: str,
54
+ placeholder: str = "カテゴリーを選択してください。",
55
+ multiselect: bool = False,
56
+ on_select: Optional[Callable[[discord.Interaction, list[discord.CategoryChannel]], Awaitable[None]]] = None
57
+ ):
58
+ """
59
+ Initialize the CategorySelectHandler.
60
+
61
+ Args:
62
+ bot: Discord bot instance
63
+ custom_id: Unique identifier for this handler
64
+ placeholder: Placeholder text for the select menu
65
+ multiselect: Whether to allow multiple selections
66
+ on_select: Callback function when categories are selected
67
+ """
68
+ self.bot: commands.Bot = bot
69
+ self.custom_id = custom_id
70
+ self.placeholder = placeholder
71
+ self.multiselect = multiselect
72
+ self.on_select = on_select
73
+
74
+ def get_custom_id(self) -> str:
75
+ return self.custom_id
76
+
77
+ def get_view(self, guild: discord.Guild, page: int = 1) -> discord.ui.View:
78
+ return get_category_select(guild, self.custom_id, self.placeholder, page, self.multiselect)
79
+
80
+ async def call(self, inter: discord.Interaction):
81
+ try:
82
+ custom_id: str = inter.data["custom_id"]
83
+
84
+ if custom_id == f"{self.custom_id}_cancel_select":
85
+ await inter.response.edit_message(content="キャンセルしました。", view=None)
86
+
87
+ elif custom_id.startswith(f"{self.custom_id}_next_select_") or custom_id.startswith(f"{self.custom_id}_back_select_"):
88
+ page: int = int(custom_id.split("_")[-1])
89
+ view = self.get_view(inter.guild, page)
90
+ if not view:
91
+ await inter.response.edit_message("カテゴリーが設定されていません。", view=None)
92
+ return
93
+ await inter.response.edit_message(content=f"{self.placeholder}", view=view)
94
+
95
+ elif custom_id.startswith(f"{self.custom_id}_"):
96
+ # セレクトメニューが選択された場合
97
+ if inter.data.get("component_type") == 3: # Select Menu
98
+ selected_ids = inter.data["values"]
99
+ selected_categories = []
100
+ for category_id in selected_ids:
101
+ category = inter.guild.get_channel(int(category_id))
102
+ if category and isinstance(category, discord.CategoryChannel):
103
+ selected_categories.append(category)
104
+
105
+ if self.on_select:
106
+ await self.on_select(inter, selected_categories)
107
+ else:
108
+ category_names = [cat.name for cat in selected_categories]
109
+ await inter.response.edit_message(
110
+ content=f"選択されたカテゴリー: {', '.join(category_names)}",
111
+ view=None
112
+ )
113
+
114
+ except Exception:
115
+ traceback.print_exc()
116
+
117
+ # グローバルなハンドラー管理辞書
118
+ _category_handlers: dict[str, CategorySelectHandler] = {}
119
+
120
+ def register_category_handler(handler: CategorySelectHandler):
121
+ """ハンドラーを登録"""
122
+ _category_handlers[handler.get_custom_id()] = handler
123
+
124
+ def get_category_handler(custom_id: str) -> Optional[CategorySelectHandler]:
125
+ """登録されたハンドラーを取得"""
126
+ for handler_id, handler in _category_handlers.items():
127
+ if custom_id.startswith(handler_id):
128
+ return handler
129
+ return None
130
+
131
+ async def handle_category_interaction(inter: discord.Interaction):
132
+ """統一されたインタラクションハンドラー"""
133
+ custom_id = inter.data["custom_id"]
134
+ handler = get_category_handler(custom_id)
135
+ if handler:
136
+ await handler.call(inter)
137
+ else:
138
+ await inter.response.send_message("ハンドラーが見つかりません。", ephemeral=True)
@@ -0,0 +1,177 @@
1
+ import discord
2
+ import traceback
3
+ from discord.ext import commands
4
+ from typing import Callable, Optional, Awaitable
5
+
6
+ def select_role(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> dict:
7
+ # @everyoneロールを除外してロール一覧を取得
8
+ role_list = [role for role in guild.roles if role.name != "@everyone"]
9
+ # 位置順でソート(上位のロールが先頭)
10
+ role_list.sort(key=lambda role: role.position, reverse=True)
11
+
12
+ total_roles = len(role_list)
13
+
14
+ # 1ページあたりの項目数
15
+ items_per_page = 25
16
+ # ページの範囲を計算
17
+ start = items_per_page * (page - 1)
18
+ end = items_per_page * page
19
+ last_page = (total_roles + items_per_page - 1) // items_per_page # ページ数を計算
20
+ print(f"start: {start}, end: {end}, total_roles: {total_roles}")
21
+ # 範囲外なら最後のページを選ぶ
22
+ if start >= total_roles:
23
+ start = items_per_page * (last_page - 1)
24
+ end = total_roles
25
+ page = last_page
26
+
27
+ options = []
28
+ count: int = 0
29
+ for role in role_list[start:end]:
30
+ # ロールの色を表示に使用
31
+ emoji = "🔴" if role.color != discord.Color.default() else "⚪"
32
+ label = f"{emoji}{role.name}"
33
+ # ラベルが100文字を超える場合は切り詰める
34
+ if len(label) > 100:
35
+ label = label[:97] + "..."
36
+ options.append(discord.SelectOption(label=label, value=str(role.id)))
37
+ count += 1
38
+
39
+ if not multiselect:
40
+ count = 1
41
+
42
+ select_ui = discord.ui.Select(custom_id=select_ui_id + "_" + str(page), placeholder=placeholder, options=options, max_values=count)
43
+ return {"select_ui": select_ui, "page": page, "last_page": last_page}
44
+
45
+ def get_role_select(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> discord.ui.View:
46
+ try:
47
+ data = select_role(guild, select_ui_id, placeholder, page, multiselect)
48
+ view: discord.ui.View = discord.ui.View()
49
+ select_role_ui: discord.ui.Select = data["select_ui"]
50
+ view.add_item(select_role_ui)
51
+
52
+ page = data["page"]
53
+ last_page = data["last_page"]
54
+
55
+ prev_button: discord.ui.Button = discord.ui.Button(
56
+ style=discord.ButtonStyle.red,
57
+ label="前へ",
58
+ custom_id=f"{select_ui_id}_back_select_{page - 1}",
59
+ disabled=page <= 1
60
+ )
61
+ view.add_item(prev_button)
62
+
63
+ next_button: discord.ui.Button = discord.ui.Button(
64
+ style=discord.ButtonStyle.green,
65
+ label="次へ",
66
+ custom_id=f"{select_ui_id}_next_select_{page + 1}",
67
+ disabled=page >= last_page
68
+ )
69
+ view.add_item(next_button)
70
+
71
+ cancel_button: discord.ui.Button = discord.ui.Button(
72
+ style=discord.ButtonStyle.red,
73
+ label="キャンセル",
74
+ custom_id=f"{select_ui_id}_cancel_select"
75
+ )
76
+ view.add_item(cancel_button)
77
+
78
+ return view
79
+ except Exception as e:
80
+ print(f"Error in get_role_select: {e}")
81
+ traceback.print_exc()
82
+ return None
83
+
84
+ class RoleSelectHandler:
85
+ def __init__(self,
86
+ bot: commands.Bot,
87
+ custom_id: str,
88
+ placeholder: str = "ロールを選択してください。",
89
+ multiselect: bool = False,
90
+ on_select: Optional[Callable[[discord.Interaction, list[discord.Role]], Awaitable[None]]] = None
91
+ ):
92
+ """
93
+ Initialize the RoleSelectHandler.
94
+
95
+ Args:
96
+ bot: Discord bot instance
97
+ custom_id: Unique identifier for this handler
98
+ placeholder: Placeholder text for the select menu
99
+ multiselect: Whether to allow multiple selections
100
+ on_select: Callback function when roles are selected
101
+ """
102
+ self.bot: commands.Bot = bot
103
+ self.custom_id = custom_id
104
+ self.placeholder = placeholder
105
+ self.multiselect = multiselect
106
+ self.on_select = on_select
107
+
108
+ def get_custom_id(self) -> str:
109
+ return self.custom_id
110
+
111
+ def get_view(self, guild: discord.Guild, page: int = 1) -> discord.ui.View:
112
+ return get_role_select(guild, self.custom_id, self.placeholder, page, self.multiselect)
113
+
114
+ async def call(self, inter: discord.Interaction):
115
+ try:
116
+ custom_id: str = inter.data["custom_id"]
117
+
118
+ if custom_id == f"{self.custom_id}_cancel_select":
119
+ await inter.response.edit_message(content="キャンセルしました。", view=None)
120
+
121
+ elif custom_id.startswith(f"{self.custom_id}_next_select_") or custom_id.startswith(f"{self.custom_id}_back_select_"):
122
+ page: int = int(custom_id.split("_")[-1])
123
+ view = self.get_view(inter.guild, page)
124
+ if not view:
125
+ await inter.response.edit_message("ロールが設定されていません。", view=None)
126
+ return
127
+ await inter.response.edit_message(content=f"{self.placeholder}", view=view)
128
+
129
+ elif custom_id.startswith(f"{self.custom_id}_"):
130
+ # セレクトメニューが選択された場合
131
+ if inter.data.get("component_type") == 3: # Select Menu
132
+ selected_ids = inter.data["values"]
133
+ selected_roles = []
134
+ for role_id in selected_ids:
135
+ role = inter.guild.get_role(int(role_id))
136
+ if role:
137
+ selected_roles.append(role)
138
+
139
+ if self.on_select:
140
+ await self.on_select(inter, selected_roles)
141
+ else:
142
+ role_names = [role.name for role in selected_roles]
143
+ await inter.response.edit_message(
144
+ content=f"選択されたロール: {', '.join(role_names)}",
145
+ view=None
146
+ )
147
+
148
+ except Exception:
149
+ traceback.print_exc()
150
+
151
+ # グローバルなハンドラー管理辞書
152
+ _role_handlers: dict[str, RoleSelectHandler] = {}
153
+
154
+ def register_role_handler(handler: RoleSelectHandler):
155
+ """ハンドラーを登録"""
156
+ _role_handlers[handler.get_custom_id()] = handler
157
+
158
+ def get_role_handler(custom_id: str) -> Optional[RoleSelectHandler]:
159
+ """登録されたハンドラーを取得"""
160
+ for handler_id, handler in _role_handlers.items():
161
+ if custom_id.startswith(handler_id):
162
+ return handler
163
+ return None
164
+
165
+ async def handle_role_interaction(inter: discord.Interaction):
166
+ """統一されたインタラクションハンドラー"""
167
+ try:
168
+ custom_id = inter.data["custom_id"]
169
+ handler = get_role_handler(custom_id)
170
+ if handler:
171
+ await handler.call(inter)
172
+ else:
173
+ await inter.response.send_message("ハンドラーが見つかりません。", ephemeral=True)
174
+ except Exception as e:
175
+ print(f"Error in handle_role_interaction: {e}")
176
+ traceback.print_exc()
177
+ await inter.response.send_message("エラーが発生しました。", ephemeral=True)
@@ -0,0 +1,175 @@
1
+ import discord
2
+ import traceback
3
+ from discord.ext import commands
4
+ from typing import Callable, Optional, Awaitable
5
+
6
+ def select_vc(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> dict:
7
+ # ボイスチャンネルの一覧を取得
8
+ vc_list = [channel for channel in guild.channels if isinstance(channel, discord.VoiceChannel)]
9
+ # カテゴリー順、位置順でソート
10
+ vc_list.sort(key=lambda vc: (vc.category.name if vc.category else "", vc.position))
11
+
12
+ total_vcs = len(vc_list)
13
+
14
+ # 1ページあたりの項目数
15
+ items_per_page = 25
16
+ # ページの範囲を計算
17
+ start = items_per_page * (page - 1)
18
+ end = items_per_page * page
19
+ last_page = (total_vcs + items_per_page - 1) // items_per_page # ページ数を計算
20
+ print(f"start: {start}, end: {end}, total_vcs: {total_vcs}")
21
+ # 範囲外なら最後のページを選ぶ
22
+ if start >= total_vcs:
23
+ start = items_per_page * (last_page - 1)
24
+ end = total_vcs
25
+ page = last_page
26
+
27
+ options = []
28
+ count: int = 0
29
+ for vc in vc_list[start:end]:
30
+ # カテゴリー名を含めて表示
31
+ if vc.category:
32
+ label = f"🔊[{vc.category.name}] {vc.name}"
33
+ else:
34
+ label = f"🔊{vc.name}"
35
+
36
+ # ラベルが100文字を超える場合は切り詰める
37
+ if len(label) > 100:
38
+ label = label[:97] + "..."
39
+
40
+ options.append(discord.SelectOption(label=label, value=str(vc.id)))
41
+ count += 1
42
+
43
+ if not multiselect:
44
+ count = 1
45
+
46
+ select_ui = discord.ui.Select(custom_id=select_ui_id + "_" + str(page), placeholder=placeholder, options=options, max_values=count)
47
+ return {"select_ui": select_ui, "page": page, "last_page": last_page}
48
+
49
+ def get_vc_select(guild: discord.Guild, select_ui_id: str, placeholder: str, page: int = 1, multiselect: bool = False) -> discord.ui.View:
50
+ try:
51
+ data = select_vc(guild, select_ui_id, placeholder, page, multiselect)
52
+ view: discord.ui.View = discord.ui.View()
53
+ select_vc_ui: discord.ui.Select = data["select_ui"]
54
+ view.add_item(select_vc_ui)
55
+
56
+ page = data["page"]
57
+ last_page = data["last_page"]
58
+
59
+ prev_button: discord.ui.Button = discord.ui.Button(
60
+ style=discord.ButtonStyle.red,
61
+ label="前へ",
62
+ custom_id=f"{select_ui_id}_back_select_{page - 1}",
63
+ disabled=page <= 1
64
+ )
65
+ view.add_item(prev_button)
66
+
67
+ next_button: discord.ui.Button = discord.ui.Button(
68
+ style=discord.ButtonStyle.green,
69
+ label="次へ",
70
+ custom_id=f"{select_ui_id}_next_select_{page + 1}",
71
+ disabled=page >= last_page
72
+ )
73
+ view.add_item(next_button)
74
+
75
+ cancel_button: discord.ui.Button = discord.ui.Button(
76
+ style=discord.ButtonStyle.red,
77
+ label="キャンセル",
78
+ custom_id=f"{select_ui_id}_cancel_select"
79
+ )
80
+ view.add_item(cancel_button)
81
+
82
+ return view
83
+ except Exception as e:
84
+ print(f"Error in get_vc_select: {e}")
85
+ traceback.print_exc()
86
+ return None
87
+
88
+ class VCSelectHandler:
89
+ def __init__(self,
90
+ bot: commands.Bot,
91
+ custom_id: str,
92
+ placeholder: str = "ボイスチャンネルを選択してください。",
93
+ multiselect: bool = False,
94
+ on_select: Optional[Callable[[discord.Interaction, list[discord.VoiceChannel]], Awaitable[None]]] = None):
95
+ """
96
+ Initialize the VCSelectHandler.
97
+
98
+ Args:
99
+ bot: Discord bot instance
100
+ custom_id: Unique identifier for this handler
101
+ placeholder: Placeholder text for the select menu
102
+ multiselect: Whether to allow multiple selections
103
+ on_select: Callback function when voice channels are selected
104
+ """
105
+ self.bot: commands.Bot = bot
106
+ self.custom_id = custom_id
107
+ self.placeholder = placeholder
108
+ self.multiselect = multiselect
109
+ self.on_select = on_select
110
+
111
+ def get_custom_id(self) -> str:
112
+ return self.custom_id
113
+
114
+ def get_view(self, guild: discord.Guild, page: int = 1) -> discord.ui.View:
115
+ return get_vc_select(guild, self.custom_id, self.placeholder, page, self.multiselect)
116
+
117
+ async def call(self, inter: discord.Interaction):
118
+ try:
119
+ custom_id: str = inter.data["custom_id"]
120
+
121
+ if custom_id == f"{self.custom_id}_cancel_select":
122
+ await inter.response.edit_message(content="キャンセルしました。", view=None)
123
+
124
+ elif custom_id.startswith(f"{self.custom_id}_next_select_") or custom_id.startswith(f"{self.custom_id}_back_select_"):
125
+ page: int = int(custom_id.split("_")[-1])
126
+ view = self.get_view(inter.guild, page)
127
+ if not view:
128
+ await inter.response.edit_message("ボイスチャンネルが設定されていません。", view=None)
129
+ return
130
+ await inter.response.edit_message(content=f"{self.placeholder}", view=view)
131
+
132
+ elif custom_id.startswith(f"{self.custom_id}_"):
133
+ # セレクトメニューが選択された場合
134
+ if inter.data.get("component_type") == 3: # Select Menu
135
+ selected_ids = inter.data["values"]
136
+ selected_vcs = []
137
+ for vc_id in selected_ids:
138
+ vc = inter.guild.get_channel(int(vc_id))
139
+ if vc and isinstance(vc, discord.VoiceChannel):
140
+ selected_vcs.append(vc)
141
+
142
+ if self.on_select:
143
+ await self.on_select(inter, selected_vcs)
144
+ else:
145
+ vc_names = [vc.name for vc in selected_vcs]
146
+ await inter.response.edit_message(
147
+ content=f"選択されたボイスチャンネル: {', '.join(vc_names)}",
148
+ view=None
149
+ )
150
+
151
+ except Exception:
152
+ traceback.print_exc()
153
+
154
+ # グローバルなハンドラー管理辞書
155
+ _vc_handlers: dict[str, VCSelectHandler] = {}
156
+
157
+ def register_vc_handler(handler: VCSelectHandler):
158
+ """ハンドラーを登録"""
159
+ _vc_handlers[handler.get_custom_id()] = handler
160
+
161
+ def get_vc_handler(custom_id: str) -> Optional[VCSelectHandler]:
162
+ """登録されたハンドラーを取得"""
163
+ for handler_id, handler in _vc_handlers.items():
164
+ if custom_id.startswith(handler_id):
165
+ return handler
166
+ return None
167
+
168
+ async def handle_vc_interaction(inter: discord.Interaction):
169
+ """統一されたインタラクションハンドラー"""
170
+ custom_id = inter.data["custom_id"]
171
+ handler = get_vc_handler(custom_id)
172
+ if handler:
173
+ await handler.call(inter)
174
+ else:
175
+ await inter.response.send_message("ハンドラーが見つかりません。", ephemeral=True)
@@ -0,0 +1,238 @@
1
+ import discord
2
+ import traceback
3
+ from typing import Union, Optional
4
+
5
+ async def send_webhook_message(
6
+ channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
7
+ content: str = None,
8
+ embed: discord.Embed = None,
9
+ embeds: list[discord.Embed] = None,
10
+ username: str = "Webhook Bot",
11
+ avatar_url: str = None,
12
+ file: discord.File = None,
13
+ files: list[discord.File] = None
14
+ ) -> Optional[discord.WebhookMessage]:
15
+ """
16
+ 指定されたチャンネルにWebhookメッセージを送信する
17
+
18
+ Args:
19
+ channel: 送信先チャンネル (TextChannel/VoiceChannel/Thread)
20
+ content: メッセージ内容
21
+ embed: 埋め込み (単体)
22
+ embeds: 埋め込み (複数)
23
+ username: Webhookの表示名
24
+ avatar_url: Webhookのアイコン画像URL
25
+ file: 添付ファイル (単体)
26
+ files: 添付ファイル (複数)
27
+
28
+ Returns:
29
+ WebhookMessage: 送信されたメッセージ (失敗時はNone)
30
+ """
31
+ try:
32
+ webhook = None
33
+ target_channel = None
34
+
35
+ # チャンネルタイプに応じて処理を分岐
36
+ if isinstance(channel, discord.Thread):
37
+ # スレッドの場合、親チャンネルのWebhookを使用
38
+ target_channel = channel.parent
39
+ if not isinstance(target_channel, (discord.TextChannel, discord.VoiceChannel)):
40
+ print(f"Error: スレッドの親チャンネルが無効です: {target_channel}")
41
+ return None
42
+ elif isinstance(channel, discord.VoiceChannel):
43
+ # VCの場合、VCチャットのWebhookを使用
44
+ target_channel = channel
45
+ elif isinstance(channel, discord.TextChannel):
46
+ # テキストチャンネルの場合
47
+ target_channel = channel
48
+ else:
49
+ print(f"Error: サポートされていないチャンネルタイプです: {type(channel)}")
50
+ return None
51
+
52
+ # 既存のWebhookを検索
53
+ webhooks = await target_channel.webhooks()
54
+ for wh in webhooks:
55
+ if wh.name == "MPEventBot Webhook":
56
+ webhook = wh
57
+ break
58
+
59
+ # Webhookが存在しない場合は新規作成
60
+ if not webhook:
61
+ try:
62
+ webhook = await target_channel.create_webhook(
63
+ name="MPEventBot Webhook",
64
+ reason="MPEventBot用のWebhook"
65
+ )
66
+ print(f"新しいWebhookを作成しました: {target_channel.name}")
67
+ except discord.Forbidden:
68
+ print(f"Error: Webhook作成権限がありません: {target_channel.name}")
69
+ return None
70
+ except discord.HTTPException as e:
71
+ print(f"Error: Webhook作成に失敗しました: {e}")
72
+ return None
73
+
74
+ # メッセージ送信の準備
75
+ kwargs = {
76
+ "username": username,
77
+ "wait": True # 送信されたメッセージを取得するため
78
+ }
79
+
80
+ if avatar_url:
81
+ kwargs["avatar_url"] = avatar_url
82
+
83
+ if content:
84
+ kwargs["content"] = content
85
+
86
+ if embed:
87
+ kwargs["embed"] = embed
88
+ elif embeds:
89
+ kwargs["embeds"] = embeds
90
+
91
+ if file:
92
+ kwargs["file"] = file
93
+ elif files:
94
+ kwargs["files"] = files
95
+
96
+ # スレッドの場合はthread_idを指定
97
+ if isinstance(channel, discord.Thread):
98
+ kwargs["thread"] = channel
99
+
100
+ # メッセージ送信
101
+ message = await webhook.send(**kwargs)
102
+ print(f"Webhookメッセージを送信しました: {target_channel.name}")
103
+ return message
104
+
105
+ except discord.Forbidden:
106
+ print(f"Error: 権限が不足しています: {channel.name if hasattr(channel, 'name') else channel}")
107
+ return None
108
+ except discord.HTTPException as e:
109
+ print(f"Error: HTTP例外が発生しました: {e}")
110
+ return None
111
+ except Exception as e:
112
+ print(f"Error: 予期しないエラーが発生しました: {e}")
113
+ traceback.print_exc()
114
+ return None
115
+
116
+ async def send_webhook_message_simple(
117
+ channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
118
+ content: str,
119
+ username: str = "MPEventBot",
120
+ avatar_url: str = None
121
+ ) -> Optional[discord.WebhookMessage]:
122
+ """
123
+ シンプルなWebhookメッセージ送信 (テキストのみ)
124
+
125
+ Args:
126
+ channel: 送信先チャンネル
127
+ content: メッセージ内容
128
+ username: Webhookの表示名
129
+ avatar_url: Webhookのアイコン画像URL
130
+
131
+ Returns:
132
+ WebhookMessage: 送信されたメッセージ (失敗時はNone)
133
+ """
134
+ return await send_webhook_message(
135
+ channel=channel,
136
+ content=content,
137
+ username=username,
138
+ avatar_url=avatar_url
139
+ )
140
+
141
+ async def send_webhook_embed(
142
+ channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
143
+ embed: discord.Embed,
144
+ username: str = "MPEventBot",
145
+ avatar_url: str = None
146
+ ) -> Optional[discord.WebhookMessage]:
147
+ """
148
+ Embed付きWebhookメッセージ送信
149
+
150
+ Args:
151
+ channel: 送信先チャンネル
152
+ embed: 埋め込み
153
+ username: Webhookの表示名
154
+ avatar_url: Webhookのアイコン画像URL
155
+
156
+ Returns:
157
+ WebhookMessage: 送信されたメッセージ (失敗時はNone)
158
+ """
159
+ return await send_webhook_message(
160
+ channel=channel,
161
+ embed=embed,
162
+ username=username,
163
+ avatar_url=avatar_url
164
+ )
165
+
166
+ async def send_webhook_file(
167
+ channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
168
+ file: discord.File,
169
+ content: str = None,
170
+ username: str = "MPEventBot",
171
+ avatar_url: str = None
172
+ ) -> Optional[discord.WebhookMessage]:
173
+ """
174
+ ファイル付きWebhookメッセージ送信
175
+
176
+ Args:
177
+ channel: 送信先チャンネル
178
+ file: 添付ファイル
179
+ content: メッセージ内容 (オプション)
180
+ username: Webhookの表示名
181
+ avatar_url: Webhookのアイコン画像URL
182
+
183
+ Returns:
184
+ WebhookMessage: 送信されたメッセージ (失敗時はNone)
185
+ """
186
+ return await send_webhook_message(
187
+ channel=channel,
188
+ content=content,
189
+ file=file,
190
+ username=username,
191
+ avatar_url=avatar_url
192
+ )
193
+
194
+ # 使用例
195
+ """
196
+ # テキストチャンネルに送信
197
+ channel = bot.get_channel(channel_id)
198
+ await send_webhook_message_simple(
199
+ channel=channel,
200
+ content="こんにちは!",
201
+ username="カスタムBot",
202
+ avatar_url="https://example.com/avatar.png"
203
+ )
204
+
205
+ # VCチャットに送信
206
+ vc = bot.get_channel(vc_id)
207
+ await send_webhook_message_simple(
208
+ channel=vc,
209
+ content="VCチャットメッセージ",
210
+ username="VC Bot"
211
+ )
212
+
213
+ # スレッドに送信
214
+ thread = channel.get_thread(thread_id)
215
+ await send_webhook_message_simple(
216
+ channel=thread,
217
+ content="スレッドメッセージ",
218
+ username="Thread Bot"
219
+ )
220
+
221
+ # Embed付きで送信
222
+ embed = discord.Embed(title="タイトル", description="説明", color=0x00ff00)
223
+ await send_webhook_embed(
224
+ channel=channel,
225
+ embed=embed,
226
+ username="Embed Bot",
227
+ avatar_url="https://example.com/avatar.png"
228
+ )
229
+
230
+ # ファイル付きで送信
231
+ file = discord.File("path/to/file.txt")
232
+ await send_webhook_file(
233
+ channel=channel,
234
+ file=file,
235
+ content="ファイルを送信します",
236
+ username="File Bot"
237
+ )
238
+ """
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.2
2
+ Name: discord-py-help-lib
3
+ Version: 0.0.1
4
+ Summary: A library to assist with Discord.py development
5
+ Home-page: https://github.com/hashimotok-ecsv/discord-py-help-lib
6
+ Download-URL: https://github.com/hashimotok-ecsv/discord-py-help-lib
7
+ Author: hashimotok
8
+ Author-email: contact@hashimotok.dev
9
+ License: MIT
10
+ Project-URL: Documentation, https://discord-py-help-lib.readthedocs.io/
11
+ Project-URL: Source, https://github.com/hashimotok-ecsv/discord-py-help-lib
12
+ Project-URL: Tracker, https://github.com/hashimotok-ecsv/discord-py-help-lib/issues
13
+ Keywords: discord.py help library
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: OS Independent
21
+ Requires-Python: >=3.10.6
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: discord.py>=2.5.2
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: download-url
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: project-url
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ # Auxiliary for discord.py Library
40
+
41
+ ![PyPI version](https://img.shields.io/pypi/v/discord-py-auxiliary-lib.svg)
42
+ ![Python version](https://img.shields.io/pypi/pyversions/discord-py-auxiliary-lib.svg)
43
+ ![License](https://img.shields.io/pypi/l/discord-py-auxiliary-lib.svg)
44
+
45
+ ---
46
+
47
+ ## 📦 概要
48
+
49
+ `discord-py-auxiliary-lib` は、discord.pyで役職パネルを簡単に実装するための Python ライブラリです。
50
+
51
+ 主な機能は以下のとおりです:
52
+
53
+ - 後日更新
54
+
55
+ ---
56
+
57
+ ## ✨ 特徴
58
+
59
+ - ✅ 簡単に使用が可能
60
+
61
+ ---
62
+
63
+ ## 🔧 インストール
64
+
65
+ ### PyPIからインストール:
66
+ ```bash
67
+ pip install discord-py-auxiliary-lib
68
+ ```
69
+ ### githubからインストール:
70
+ ```bash
71
+ pip install git+https://github.com/hashimotok-ecsv/discord_py_auxiliary_lib.git
72
+ ```
73
+ ## 使い方
74
+ ```python
75
+ # 後日更新
76
+ ```
77
+ #### 管理者用
78
+ ##### 更新方法
79
+ ```bash
80
+ py setup.py sdist
81
+ py setup.py bdist_wheel
82
+ py -m twine upload --repository testpypi dist/*
83
+ py -m twine upload --repository pypi dist/*
84
+ ```
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ setup.py
4
+ discord_py_help_lib/__init__.py
5
+ discord_py_help_lib/webhook_sender.py
6
+ discord_py_help_lib.egg-info/PKG-INFO
7
+ discord_py_help_lib.egg-info/SOURCES.txt
8
+ discord_py_help_lib.egg-info/dependency_links.txt
9
+ discord_py_help_lib.egg-info/entry_points.txt
10
+ discord_py_help_lib.egg-info/not-zip-safe
11
+ discord_py_help_lib.egg-info/requires.txt
12
+ discord_py_help_lib.egg-info/top_level.txt
13
+ discord_py_help_lib/select/category_select.py
14
+ discord_py_help_lib/select/role_select.py
15
+ discord_py_help_lib/select/vc_select.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ discord-py-help-lib = discord_py_help_lib.__main__:main
@@ -0,0 +1 @@
1
+ discord.py>=2.5.2
@@ -0,0 +1 @@
1
+ discord_py_help_lib
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,77 @@
1
+ from setuptools import setup
2
+ import re
3
+
4
+ def derive_version() -> str:
5
+ version = ''
6
+ with open('discord_py_help_lib/__init__.py', encoding='utf-8') as f:
7
+ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1)
8
+
9
+ if not version:
10
+ raise RuntimeError('version is not set')
11
+
12
+ if version.endswith(('a', 'b', 'rc')):
13
+ # append version identifier based on commit count
14
+ try:
15
+ import subprocess
16
+
17
+ p = subprocess.Popen(['git', 'rev-list', '--count', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
18
+ out, err = p.communicate()
19
+ if out:
20
+ version += out.decode('utf-8').strip()
21
+ p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
22
+ out, err = p.communicate()
23
+ if out:
24
+ version += '+g' + out.decode('utf-8').strip()
25
+ except Exception:
26
+ pass
27
+
28
+ return version
29
+
30
+ NAME='discord-py-help-lib'
31
+
32
+ PACKAGES = [
33
+ 'discord_py_help_lib',
34
+ 'discord_py_help_lib.select'
35
+ ]
36
+
37
+ setup(
38
+ name=NAME,
39
+ version=derive_version(),
40
+ packages=PACKAGES,
41
+ install_requires=[
42
+ 'discord.py>=2.5.2',
43
+ # Add other dependencies here
44
+ ],
45
+ author='hashimotok',
46
+ author_email='contact@hashimotok.dev',
47
+ url=f'https://github.com/hashimotok-ecsv/{NAME}',
48
+ download_url=f'https://github.com/hashimotok-ecsv/{NAME}',
49
+ python_requires=">=3.10.6",
50
+ description='A library to assist with Discord.py development',
51
+ long_description=open('README.md', encoding='utf-8').read(),
52
+ long_description_content_type='text/markdown',
53
+ classifiers=[
54
+ 'Development Status :: 5 - Production/Stable',
55
+ 'Intended Audience :: Developers',
56
+ 'License :: OSI Approved :: MIT License',
57
+ 'Programming Language :: Python :: 3.10',
58
+ 'Programming Language :: Python :: 3.11',
59
+ 'Programming Language :: Python :: 3.12',
60
+ 'Operating System :: OS Independent',
61
+ ],
62
+ keywords='discord.py help library',
63
+ include_package_data=True,
64
+ zip_safe=False,
65
+ entry_points={
66
+ 'console_scripts': [
67
+ 'discord-py-help-lib=discord_py_help_lib.__main__:main',
68
+ ],
69
+ },
70
+ project_urls={
71
+ 'Documentation': f'https://{NAME}.readthedocs.io/',
72
+ 'Source': f'https://github.com/hashimotok-ecsv/{NAME}',
73
+ 'Tracker': f'https://github.com/hashimotok-ecsv/{NAME}/issues',
74
+ },
75
+ license='MIT',
76
+ license_files=('LICENSE',),
77
+ )