Skip to content

Commit 4f53253

Browse files
AmbratolmAmbratolm
authored andcommitted
Added ai incite command
1 parent 1577c17 commit 4f53253

File tree

5 files changed

+124
-35
lines changed

5 files changed

+124
-35
lines changed

api/main.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ async def lifespan(self: ActApi):
5353
@property
5454
def info_text(self):
5555
scheme, netloc = self.address.scheme, self.address.netloc
56-
full_url = lambda txt=None: f"{Fore.BLUE}{scheme}://{netloc}{txt}{Fore.RESET}"
56+
full_url = (
57+
lambda txt=None: f"{Fore.GREEN}{scheme}://{netloc}{txt or ""}{Fore.RESET}"
58+
)
5759
output = "Links:"
5860
output += f"\n• Root: {full_url()}"
5961
output += f"\n• Swagger: {full_url(self.docs_url)}"

api/routers/member_router.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,39 @@ def __init__(self, bot: ActBot, *args, **kwargs):
1616
super().__init__(prefix="/members", tags=["Members"], *args, **kwargs)
1717

1818
def member_dict(member: Member):
19-
return {
20-
"id": member.id,
21-
"name": member.name,
22-
"display_name": member.display_name,
23-
"diplay_avatar_url": member.display_avatar.url,
24-
"display_icon_url": member.display_icon,
25-
"banner_url": member.banner.url if member.banner else None,
26-
"created_at": member.created_at,
27-
"joined_at": member.joined_at,
28-
}
19+
return (
20+
{
21+
"id": member.id,
22+
"name": member.name,
23+
"display_name": member.display_name,
24+
"diplay_avatar_url": member.display_avatar.url,
25+
"display_icon_url": member.display_icon,
26+
"banner_url": member.banner.url if member.banner else None,
27+
"created_at": member.created_at,
28+
"joined_at": member.joined_at,
29+
}
30+
if member
31+
else {}
32+
)
2933

3034
# ----------------------------------------------------------------------------------------------------
3135

32-
@self.get("/{guild_id}/", response_model=list[dict])
33-
async def get_members(guild_id: int):
36+
@self.get("/{guild_id}/top", response_model=list[dict])
37+
async def get_top_members(guild_id: int, limit: int = 10):
3438
try:
3539
for guild in bot.guilds:
3640
if guild.id == guild_id:
37-
return [member_dict(member) for member in guild.members]
38-
41+
top_actors = cast(
42+
BoardCog, bot.get_cog(BoardCog.__cog_name__)
43+
).get_top_actors(guild, limit)
44+
45+
return [
46+
member_dict(
47+
guild.get_member(actor.id)
48+
or await guild.fetch_member(actor.id)
49+
)
50+
for actor in top_actors
51+
]
3952
raise HTTPException(
4053
status_code=404,
4154
detail=f"No guild with id '{guild_id}' found among the guilds the discord bot is member of.",

bot/cogs/ai_cog.py

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,26 @@
22
import tomllib
33
from datetime import UTC, datetime
44

5-
from discord import Guild, Interaction, Member, Message, User, app_commands
5+
from discord import (
6+
DMChannel,
7+
GroupChannel,
8+
Guild,
9+
Interaction,
10+
Member,
11+
Message,
12+
TextChannel,
13+
Thread,
14+
User,
15+
app_commands,
16+
)
17+
from discord.abc import Messageable
618
from discord.ext import tasks
719
from discord.ext.commands import Cog
820
from google.genai.errors import APIError
921
from odmantic import query
1022

1123
from bot.main import ActBot
24+
from bot.ui import EmbedX
1225
from db.actor import Actor
1326
from db.main import DbRef
1427
from utils.ai import ActAi
@@ -22,7 +35,7 @@
2235
# ----------------------------------------------------------------------------------------------------
2336
# * AI Cog
2437
# ----------------------------------------------------------------------------------------------------
25-
class AI(Cog, description="Integrated generative AI chat bot."):
38+
class AiCog(Cog, description="Integrated generative AI chat bot."):
2639
CONFIG_PATH = pathlib.Path(__file__).parent / "ai_cog.toml"
2740
MAX_FILE_SIZE = 524288 # 512 KB (0.5 MB)
2841
COOLDOWN_TIME = 60 # 1 min
@@ -47,10 +60,60 @@ def cog_unload(self):
4760
# ----------------------------------------------------------------------------------------------------
4861
# * Incite
4962
# ----------------------------------------------------------------------------------------------------
50-
# @app_commands.checks.has_permissions(administrator=True)
51-
# @app_commands.command(description="")
52-
# async def incite(self, interaction: Interaction, member: Member):
53-
# pass
63+
@app_commands.guild_only()
64+
@app_commands.checks.has_permissions(administrator=True)
65+
@app_commands.default_permissions(administrator=True)
66+
@app_commands.command(description="Incite AI chat bot to interact with a member")
67+
async def incite(
68+
self, interaction: Interaction, member: Member, prompt: str | None = None
69+
):
70+
# Deny bot-self & DM & non-messageable channel
71+
if (
72+
self.bot.user == member
73+
or not interaction.guild
74+
or not isinstance(interaction.channel, Messageable)
75+
):
76+
await interaction.response.send_message(
77+
embed=EmbedX.warning("This command cannot be used in this context."),
78+
ephemeral=True,
79+
)
80+
return
81+
82+
# Prepare prompt
83+
text_prompt = (
84+
f"Begin natural talk w/ {member.mention} that feels like ur own initiative."
85+
f"Absolutely avoid references to instructions, prompts, or being told to message them."
86+
)
87+
if prompt:
88+
text_prompt += f'follow this prompt:"{prompt.strip()}".'
89+
90+
# Save current member who interacted to be remembered next time
91+
self.save_actor_interaction(interaction.guild, member)
92+
93+
# Load members who interacted before
94+
actors = self.load_actors(interaction.guild)
95+
if actors:
96+
text_prompt += f"\n{text_csv(actors, "|")}"
97+
98+
# Perform prompt
99+
log.info(f"[Prompt] {text_prompt}")
100+
try:
101+
self.ai.use_session(
102+
interaction.guild.id, history=self.load_history(interaction.guild)
103+
)
104+
async with interaction.channel.typing():
105+
await interaction.response.send_message(
106+
self.ai.prompt(text=text_prompt) or f"{member.mention} 👋"
107+
)
108+
except Exception as e:
109+
await interaction.response.send_message(
110+
embed=EmbedX.error(str(e)), ephemeral=True
111+
)
112+
log.exception(e)
113+
return
114+
115+
# Save history
116+
self.save_history(interaction.guild)
54117

55118
# ----------------------------------------------------------------------------------------------------
56119
# * On Message
@@ -103,12 +166,15 @@ async def on_message(self, message: Message):
103166
f"{message.content.replace(self.bot.user.mention, "").strip()}"
104167
)
105168

106-
# Add members who interacted before
169+
# Save current member who interacted to be remembered next time
170+
self.save_actor_interaction(message.guild, message.author)
171+
172+
# Load members who interacted before
107173
actors = self.load_actors(message.guild)
108174
if actors:
109175
text_prompt += f"\n{text_csv(actors, "|")}"
110176

111-
# Prompt AI
177+
# Perform prompt
112178
log.info(f"[Prompt] {text_prompt} <{file_prompt}>")
113179
try:
114180
self.ai.use_session(
@@ -131,8 +197,7 @@ async def on_message(self, message: Message):
131197
fallback_reply = "What? 😕"
132198
await message.reply(reply or fallback_reply)
133199

134-
# Save current member who interacted to be remembered next time
135-
self.save_actor_interaction(message.guild, message.author)
200+
# Save current chat session
136201
self.save_history(message.guild)
137202

138203
# ----------------------------------------------------------------------------------------------------

bot/cogs/help_cog.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from discord import Color, Embed, Interaction, app_commands
1+
from discord import Color, Embed, Interaction, Member, app_commands
22
from discord.ext.commands import Cog, GroupCog
33
from tabulate import tabulate
44

@@ -28,15 +28,23 @@ async def commands(self, interaction: Interaction):
2828
color=Color.blue(),
2929
)
3030
for cmd in all_cmds:
31-
embed.add_field(
32-
name=f"/{cmd.name}",
33-
value=(
34-
cmd.description
35-
if not isinstance(cmd, app_commands.commands.ContextMenu)
36-
else ""
37-
),
38-
inline=False,
39-
)
31+
# Check if command has administrator requirement in default_permissions
32+
admin_required = False
33+
if hasattr(cmd, "default_permissions") and cmd.default_permissions:
34+
admin_required = cmd.default_permissions.administrator
35+
if not admin_required or (
36+
isinstance(interaction.user, Member)
37+
and interaction.user.guild_permissions.administrator
38+
):
39+
embed.add_field(
40+
name=f"/{cmd.name}",
41+
value=(
42+
cmd.description
43+
if not isinstance(cmd, app_commands.commands.ContextMenu)
44+
else ""
45+
),
46+
inline=False,
47+
)
4048
if self.bot.user:
4149
embed.set_thumbnail(url=self.bot.user.display_avatar)
4250
await interaction.response.send_message(embed=embed)

bot/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ async def on_error(
8585
await interaction.response.send_message(embed=embed, ephemeral=True)
8686
else:
8787
await interaction.followup.send(embed=embed, ephemeral=True)
88+
log.exception(error)
8889

8990
# ----------------------------------------------------------------------------------------------------
9091

0 commit comments

Comments
 (0)