Coverage for kryptic_cypher/bot.py: 32%
91 statements
« prev ^ index » next coverage.py v7.14.2, created at 2026-06-20 18:52 +0000
« prev ^ index » next coverage.py v7.14.2, created at 2026-06-20 18:52 +0000
1import discord
2from discord import app_commands
3from discord.ext import commands
5from pydantic_settings import BaseSettings
7from kryptic_cypher.cypher.base import Cypher, CypherWithKey
9from .cypher import registered_cyphers
11intents = discord.Intents.default()
12intents.message_content = True
14bot = commands.Bot(command_prefix="!", intents=intents)
17class DiscordBotConfig(BaseSettings):
18 DISCORD_BOT_TOKEN: str
21@bot.event
22async def on_ready():
23 print(f"Logged in as {bot.user.name}")
24 try:
25 # Syncs the tree to Discord so commands appear in the client
26 synced = await bot.tree.sync()
27 print(f"Synced {len(synced)} command(s)")
28 except Exception as e:
29 print(f"Failed to sync commands: {e}")
32@bot.tree.command(name="ping", description="Replies with a pong!")
33async def ping(interaction: discord.Interaction):
34 # Use interaction.response.send_message instead of ctx.send
35 await interaction.response.send_message("🏓 Pong!")
38class EncodeModal(discord.ui.Modal, title="Encode Message"):
39 message = discord.ui.TextInput(
40 label="Message",
41 placeholder="Message to encode",
42 required=True,
43 )
44 key = discord.ui.TextInput(label="Key", placeholder="Key to use", required=False)
45 cypher = discord.ui.TextInput(
46 label="Cypher",
47 placeholder="Cypher to use",
48 required=True,
49 )
51 def __init__(self):
52 super().__init__()
54 async def on_submit(self, interaction: discord.Interaction):
55 cypher = self.cypher.value
56 message = self.message.value
57 key = self.key.value
59 if cypher not in registered_cyphers:
60 await interaction.response.send_message(
61 f"Cypher is unsupported currently... {cypher} please use {', '.join(registered_cyphers.keys())}",
62 ephemeral=True,
63 )
64 return
66 cypher_instance = registered_cyphers[cypher]
67 if isinstance(cypher_instance, CypherWithKey):
68 if not key:
69 await interaction.response.send_message(
70 "You must specify a key for this cypher",
71 ephemeral=True,
72 )
73 return
74 key_validation = cypher_instance.validate_key(key)
75 if not key_validation.success:
76 await interaction.response.send_message(
77 "\n".join(key_validation.messages),
78 ephemeral=True,
79 )
80 return
81 response = cypher_instance.encode(message, key)
82 elif isinstance(cypher_instance, Cypher):
83 response = cypher_instance.encode(message)
84 else:
85 await interaction.response.send_message(
86 f"Cypher is unsupported currently... {cypher}",
87 ephemeral=True,
88 )
89 return
90 if not response.success:
91 await interaction.response.send_message(
92 f"Failed to encode message: {response.error}",
93 ephemeral=True,
94 )
95 else:
96 await interaction.response.send_message(response.new_text)
99@bot.tree.command(
100 name="encode",
101 description="Writes a message to the discord channel encoded by the specified cypher",
102)
103async def encode(
104 interaction: discord.Interaction,
105):
106 await interaction.response.send_modal(EncodeModal())
109@bot.tree.command(
110 name="decode",
111 description="Allows you to see an encoded message by decoding it with the specified cypher...",
112)
113@app_commands.describe(
114 cypher="The specific cypher to use for encoding",
115 message_id="Link to message to decode",
116 message="The message to decode",
117 key="The key to use for encoding",
118 display="Whether or not to display the decoded message",
119)
120@app_commands.choices(
121 cypher=[
122 app_commands.Choice(
123 name=cypher,
124 value=cypher,
125 )
126 for cypher in registered_cyphers.keys()
127 ]
128)
129async def decode(
130 interaction: discord.Interaction,
131 cypher: str,
132 message_id: str = None,
133 message: str = None,
134 key: str = None,
135 display: bool = False,
136):
137 if not message and not message_id:
138 await interaction.response.send_message(
139 "You must specify a message to decode either by message_id or message text",
140 ephemeral=True,
141 )
142 return
143 cypher_instance = registered_cyphers[cypher]
145 if message_id:
146 if "://discord.com" in message_id:
147 message_id = int(message_id.split("/")[-1])
148 elif message_id.isnumeric():
149 message_id = int(message_id)
150 else:
151 await interaction.response.send_message(
152 "Invalid message_id, please either paste the link to the message or the numeric id",
153 ephemeral=True,
154 )
155 return
157 discord_message = await interaction.channel.fetch_message(message_id)
158 message = discord_message.content
160 if isinstance(cypher_instance, CypherWithKey):
161 if not key:
162 await interaction.response.send_message(
163 "You must specify a key for this cypher",
164 ephemeral=True,
165 )
166 key_validation = cypher_instance.validate_key(key)
167 if not key_validation.success:
168 await interaction.response.send_message(
169 "\n".join(key_validation.messages),
170 ephemeral=True,
171 )
172 response = cypher_instance.decode(message, key)
173 elif isinstance(cypher_instance, Cypher):
174 response = cypher_instance.decode(message)
175 else:
176 await interaction.response.send_message(
177 f"Cypher is unsupported currently... {cypher}",
178 ephemeral=True,
179 )
180 return
182 if not response.success:
183 await interaction.response.send_message(
184 f"Failed to decode message: {response.error}",
185 ephemeral=True,
186 )
187 else:
188 await interaction.response.send_message(
189 response.new_text, ephemeral=not display
190 )
193def run(
194 env_file: str = None,
195):
196 config = DiscordBotConfig(
197 _env_file=env_file,
198 )
199 bot.run(config.DISCORD_BOT_TOKEN)
202if __name__ == "__main__":
203 run()