Skip to content

Commit 84c0dc1

Browse files
authored
Merge pull request #298 from KoalaBotUK/feature/shedule-activity
Feat: Schedule Activity
2 parents c2baf89 + 0d458aa commit 84c0dc1

File tree

14 files changed

+451
-91
lines changed

14 files changed

+451
-91
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ All notable changes to KoalaBot will be documented in this file. A lot of these
33
administrators
44

55
## [Unreleased]
6+
### Base
7+
- Add scheduled activities for bot owners
8+
69
## [0.5.3] - 12-04-2022
710
### TwitchAlert
811
- Fix streams delete themselves when live (again)

koala/cogs/base/cog.py

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,41 @@
1010
# Built-in/Generic Imports
1111

1212
# Libs
13-
from discord.ext import commands
13+
import datetime
14+
15+
import discord
16+
from discord.ext import commands, tasks
1417

1518
# Own modules
19+
from discord.ext.commands import BadArgument
20+
1621
import koalabot
1722
from koala.db import get_all_available_guild_extensions, give_guild_extension, \
1823
get_enabled_guild_extensions, remove_guild_extension
19-
from .utils import new_discord_activity, list_ext_embed
24+
from . import core
25+
from .utils import list_ext_embed
2026
from .log import logger
2127

2228
# Constants
2329

2430
# Variables
2531

2632

33+
def convert_activity_type(argument):
34+
try:
35+
return discord.ActivityType[argument]
36+
except KeyError:
37+
raise BadArgument('Unknown activity type %s' % argument)
38+
39+
40+
def convert_iso_datetime(argument):
41+
try:
42+
return datetime.datetime.fromisoformat(argument)
43+
except ValueError:
44+
raise BadArgument('Invalid ISO format "%s", instead use the format "2020-01-01 00:00:00"' % argument)
45+
46+
47+
2748
class BaseCog(commands.Cog, name='KoalaBot'):
2849
"""
2950
A discord.py cog with general commands useful to managers of the bot and servers
@@ -38,30 +59,97 @@ def __init__(self, bot):
3859
self._last_member = None
3960
self.started = False
4061
self.COGS_DIR = koalabot.COGS_DIR
62+
self.current_activity = None
4163

4264
@commands.Cog.listener()
4365
async def on_ready(self):
4466
"""
4567
Ran after all cogs have been started and bot is ready
4668
"""
47-
await self.bot.change_presence(activity=new_discord_activity("playing", f"{koalabot.COMMAND_PREFIX}help"))
69+
core.activity_clear_current()
70+
await self.update_activity()
71+
self.update_activity.start()
4872
self.started = True
4973
logger.info("Bot is ready.")
5074

51-
@commands.command(name="activity", aliases=["change_activity"])
75+
@commands.group(name="activity")
76+
@commands.check(koalabot.is_owner)
77+
async def activity_group(self, ctx: commands.Context):
78+
"""
79+
Group of commands for activity functionality.
80+
:param ctx: Context of the command
81+
:return:
82+
"""
83+
84+
@activity_group.command(name="set")
5285
@commands.check(koalabot.is_owner)
53-
async def change_activity(self, ctx, new_activity, name):
86+
async def activity_set(self, ctx, new_activity: convert_activity_type, name: str, url: str = None):
5487
"""
5588
Change the activity of the bot
5689
:param ctx: Context of the command
5790
:param new_activity: The new activity of the bot
5891
:param name: The name of the activity
92+
:param url: url for streaming
5993
"""
60-
if str.lower(new_activity) in ["playing", "watching", "listening", "streaming"]:
61-
await self.bot.change_presence(activity=new_discord_activity(new_activity, name))
62-
await ctx.send(f"I am now {new_activity} {name}")
63-
else:
64-
await ctx.send("That is not a valid activity, sorry!\nTry 'playing' or 'watching'")
94+
await core.activity_set(new_activity, name, url, bot=self.bot)
95+
await ctx.send(f"I am now {new_activity.name} {name}")
96+
97+
@activity_group.command(name="schedule")
98+
@commands.check(koalabot.is_owner)
99+
async def activity_schedule(self, ctx, new_activity: convert_activity_type, message: str,
100+
start_time: convert_iso_datetime, end_time: convert_iso_datetime, url: str = None):
101+
"""
102+
Schedule an activity
103+
:param ctx: Context of the command
104+
:param new_activity: activity type (watching, playing etc.)
105+
:param message: message
106+
:param start_time: iso format start time
107+
:param end_time: iso format end time
108+
:param url: url
109+
"""
110+
core.activity_schedule(new_activity, message, url, start_time, end_time)
111+
await ctx.send("Activity saved")
112+
113+
@activity_group.command(name="list")
114+
@commands.check(koalabot.is_owner)
115+
async def activity_list(self, ctx, show_all: bool = False):
116+
"""
117+
List scheduled activities
118+
:param ctx: Context of the command
119+
:param show_all: false=future activities, true=all activities
120+
"""
121+
activities = core.activity_list(show_all)
122+
result = "Activities:"
123+
for activity in activities:
124+
result += "\n%s, %s, %s, %s, %s, %s" % (activity.activity_id, activity.activity_type.name,
125+
activity.stream_url, activity.message, activity.time_start,
126+
activity.time_end)
127+
await ctx.send(result)
128+
129+
@activity_group.command(name="remove")
130+
@commands.check(koalabot.is_owner)
131+
async def activity_remove(self, ctx, activity_id: int):
132+
"""
133+
Remove an existing activity
134+
:param ctx: Context of the command
135+
:param activity_id: Activity ID
136+
"""
137+
activity = core.activity_remove(activity_id)
138+
result = "Removed:"
139+
result += "\n%s, %s, %s, %s, %s, %s" % (activity.activity_id, activity.activity_type.name,
140+
activity.stream_url, activity.message, activity.time_start,
141+
activity.time_end)
142+
await ctx.send(result)
143+
144+
@tasks.loop(seconds=1.0)
145+
async def update_activity(self):
146+
"""
147+
Loop for updating the activity of the bot according to scheduled activities
148+
"""
149+
try:
150+
await core.activity_set_current_scheduled(self.bot)
151+
except Exception as err:
152+
logger.error("Error in update_activity loop %s" % err, exc_info=err)
65153

66154
@commands.command()
67155
async def ping(self, ctx):

koala/cogs/base/core.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import datetime
2+
from typing import List, Optional
3+
4+
import discord
5+
from discord.ext.commands import Bot
6+
7+
from . import db
8+
from .log import logger
9+
from .models import ScheduledActivities
10+
from koala.db import assign_session
11+
from .utils import DEFAULT_ACTIVITY, activity_eq
12+
13+
# Constants
14+
15+
# Variables
16+
current_activity = None
17+
18+
19+
def activity_clear_current():
20+
global current_activity
21+
current_activity = None
22+
23+
24+
async def activity_set(activity_type: discord.ActivityType, name: str, url: Optional[str], bot: Bot):
25+
"""
26+
Set presence for this bot to the given presence
27+
:param activity_type:
28+
:param name:
29+
:param url:
30+
:param bot:
31+
:return:
32+
"""
33+
new_activity = discord.Activity(type=activity_type, name=name, url=url)
34+
await bot.change_presence(activity=new_activity)
35+
36+
37+
@assign_session
38+
def activity_schedule(activity_type: discord.ActivityType, message: str, url: Optional[str], start_time: datetime.datetime,
39+
end_time: datetime.datetime, **kwargs):
40+
"""
41+
Schedule an activity to be used for a timed presence
42+
:param activity_type:
43+
:param message:
44+
:param url:
45+
:param start_time:
46+
:param end_time:
47+
:param kwargs:
48+
:return:
49+
"""
50+
db.add_scheduled_activity(activity_type, message, url, start_time, end_time, **kwargs)
51+
52+
53+
@assign_session
54+
def activity_list(show_all: bool, **kwargs) -> List[ScheduledActivities]:
55+
"""
56+
Get a list of all scheduled activity
57+
:param show_all:
58+
:param kwargs:
59+
:return:
60+
"""
61+
return db.get_scheduled_activities(False, not show_all, **kwargs)
62+
63+
64+
@assign_session
65+
def activity_remove(activity_id: int, **kwargs) -> ScheduledActivities:
66+
"""
67+
Remove a scheduled activity
68+
:param activity_id:
69+
:param kwargs:
70+
:return:
71+
"""
72+
return db.remove_scheduled_activities(activity_id, **kwargs)
73+
74+
75+
@assign_session
76+
async def activity_set_current_scheduled(bot: Bot, **kwargs):
77+
"""
78+
Set the current scheduled activity as the bot presence
79+
:param bot:
80+
:param kwargs:
81+
:return:
82+
"""
83+
activities = db.get_scheduled_activities(True, True, **kwargs)
84+
if len(activities) > 1:
85+
logger.warn("Multiple activities found for this timeslot, %s" % activities)
86+
87+
if len(activities) != 0:
88+
activity = activities[0]
89+
new_activity = discord.Activity(
90+
type=activity.activity_type, name=activity.message, url=activity.stream_url)
91+
else:
92+
new_activity = DEFAULT_ACTIVITY
93+
94+
global current_activity
95+
if not activity_eq(new_activity, current_activity):
96+
await bot.change_presence(activity=new_activity)
97+
logger.info("Auto changing bot presence: %s" % new_activity)
98+
current_activity = new_activity

koala/cogs/base/db.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import datetime
2+
from typing import List, Optional
3+
4+
import discord
5+
import sqlalchemy.orm
6+
from sqlalchemy import select
7+
8+
from koala.cogs.base.models import ScheduledActivities
9+
from koala.db import assign_session
10+
11+
12+
@assign_session
13+
def add_scheduled_activity(activity_type: discord.ActivityType, message: str, url: Optional[str],
14+
time_start: datetime.datetime, time_end: datetime.datetime,
15+
session: sqlalchemy.orm.Session) -> ScheduledActivities:
16+
"""
17+
Add scheduled activity to database
18+
:param activity_type:
19+
:param message:
20+
:param url:
21+
:param time_start:
22+
:param time_end:
23+
:param session:
24+
:return:
25+
"""
26+
activity = ScheduledActivities(activity_type=activity_type, message=message, stream_url=url, time_start=time_start,
27+
time_end=time_end)
28+
session.add(activity)
29+
session.commit()
30+
return activity
31+
32+
33+
@assign_session
34+
def get_scheduled_activities(start_time_restricted: bool, end_time_restricted: bool,
35+
session: sqlalchemy.orm.Session) -> List[ScheduledActivities]:
36+
"""
37+
Get all scheduled activities
38+
:param start_time_restricted:
39+
:param end_time_restricted:
40+
:param session:
41+
:return:
42+
"""
43+
current_time = datetime.datetime.now()
44+
query = select(ScheduledActivities)
45+
if start_time_restricted:
46+
query = query.where(ScheduledActivities.time_start < current_time)
47+
if end_time_restricted:
48+
query = query.where(ScheduledActivities.time_end > current_time)
49+
50+
return session.execute(query).scalars().all()
51+
52+
53+
@assign_session
54+
def remove_scheduled_activities(activity_id: int, session: sqlalchemy.orm.Session) -> ScheduledActivities:
55+
"""
56+
Delete a specified scheduled activity from a database
57+
:param activity_id:
58+
:param session:
59+
:return:
60+
"""
61+
activity = session.execute(select(ScheduledActivities).filter_by(activity_id=activity_id)).scalar()
62+
session.delete(activity)
63+
session.commit()
64+
return activity

koala/cogs/base/models.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Futures
2+
# Built-in/Generic Imports
3+
# Libs
4+
from discord import ActivityType
5+
from sqlalchemy import Column, Integer, String, TIMESTAMP, Enum
6+
7+
# Own modules
8+
from koala.models import mapper_registry
9+
from koala.db import setup
10+
11+
# Constants
12+
13+
# Variables
14+
15+
16+
@mapper_registry.mapped
17+
class ScheduledActivities:
18+
__tablename__ = 'ScheduledActivities'
19+
activity_id = Column(Integer, primary_key=True, autoincrement=True)
20+
activity_type = Column(Enum(ActivityType), comment="0: Playing, 1: Streaming, 2: Listening, 3: Watching, 4: Custom,"
21+
" 5: Competing")
22+
stream_url = Column(String, nullable=True)
23+
message = Column(String)
24+
time_start = Column(TIMESTAMP)
25+
time_end = Column(TIMESTAMP)
26+
27+
def __repr__(self):
28+
return "<ScheduledActivities(%s, %s, %s)>" % \
29+
(self.activity_id, self.activity_type, self.message)
30+
31+
32+
setup()

0 commit comments

Comments
 (0)