Commands restructuring

- Reorganized commands into separate files for better fault tolerance and feature expandability
- Added basic dynamic bot status
  - Needs additional work. Currently very basic, planned features include fully dynamic mode with automatic alerts and such.
- Minor other tweaks
kami_dev
Kami 2025-02-23 17:00:45 +01:00
parent 78e24a4641
commit 766c3ab690
17 changed files with 438 additions and 365 deletions

View File

@ -1,9 +1,10 @@
# bot_discord.py
import discord
from discord import app_commands
from discord.ext import commands
from discord.ext import commands, tasks
import importlib
import cmd_discord
import json
import globals
@ -150,6 +151,20 @@ class DiscordBot(commands.Bot):
except Exception as e:
globals.log(f"Command processing failed: {e}", "ERROR")
def load_bot_settings(self):
"""Loads bot activity settings from JSON file."""
try:
with open("settings/discord_bot_settings.json", "r") as file:
return json.load(file)
except Exception as e:
self.log(f"Failed to load settings: {e}", "ERROR")
return {
"activity_mode": 0,
"static_activity": {"type": "Playing", "name": "with my commands!"},
"rotating_activities": [],
"dynamic_activities": {},
"rotation_interval": 600
}
async def on_command(self, ctx):
"""Logs every command execution at DEBUG level."""
@ -184,6 +199,13 @@ class DiscordBot(commands.Bot):
async def on_ready(self):
"""Runs when the bot successfully logs in."""
# Load activity settings
self.settings = self.load_bot_settings()
# Set initial bot activity
await self.update_activity()
# Sync Slash Commands
try:
# Sync slash commands globally
@ -216,6 +238,73 @@ class DiscordBot(commands.Bot):
globals.log("Discord bot has lost connection!", "WARNING")
log_bot_event(self.db_conn, "DISCORD_DISCONNECTED", "Discord bot lost connection.")
async def update_activity(self):
"""Sets the bot's activity based on settings."""
mode = self.settings["activity_mode"]
if mode == 0:
await self.change_presence(activity=None)
self.log("Activity disabled", "DEBUG")
elif mode == 1:
# Set static activity
activity_data = self.settings["static_activity"]
activity = self.get_activity(activity_data["type"], activity_data["name"])
await self.change_presence(activity=activity)
self.log(f"Static activity set: {activity_data['type']} {activity_data['name']}", "DEBUG")
elif mode == 2:
# Start rotating activity task
if not self.change_rotating_activity.is_running():
self.change_rotating_activity.start()
self.log("Rotating activity mode enabled", "DEBUG")
elif mode == 3:
# Check for dynamic activity
await self.set_dynamic_activity()
@tasks.loop(seconds=600) # Default to 10 minutes
async def change_rotating_activity(self):
"""Rotates activities every set interval."""
activities = self.settings["rotating_activities"]
if not activities:
return
# Pick the next activity
activity_data = activities.pop(0)
activities.append(activity_data) # Move to the end of the list
activity = self.get_activity(activity_data["type"], activity_data["name"])
await self.change_presence(activity=activity)
self.log(f"Rotating activity: {activity_data['type']} {activity_data['name']}", "DEBUG")
async def set_dynamic_activity(self):
"""Sets a dynamic activity based on external conditions."""
twitch_live = await modules.utility.is_channel_live(self)
if twitch_live:
activity_data = self.settings["dynamic_activities"]["twitch_live"]
else:
# activity_data = self.settings["dynamic_activities"].get("default_idle", None)
if not self.change_rotating_activity.is_running():
self.change_rotating_activity.start()
if activity_data:
activity = self.get_activity(activity_data["type"], activity_data["name"], activity_data.get("url"))
await self.change_presence(activity=activity)
self.log(f"Dynamic activity set: {activity_data['type']} {activity_data['name']}", "DEBUG")
def get_activity(self, activity_type, name, url=None):
"""Returns a discord activity object based on type, including support for Custom Status."""
activity_map = {
"Playing": discord.Game(name=name),
"Streaming": discord.Streaming(name=name, url=url or "https://twitch.tv/OokamiKunTV"),
"Listening": discord.Activity(type=discord.ActivityType.listening, name=name),
"Watching": discord.Activity(type=discord.ActivityType.watching, name=name),
"Custom": discord.CustomActivity(name=name)
}
return activity_map.get(activity_type, discord.Game(name="around in Discord"))
async def on_voice_state_update(self, member, before, after):
"""
Tracks user joins, leaves, mutes, deafens, streams, and voice channel moves.

View File

@ -1,162 +0,0 @@
# cmd_discord.py
import discord
from discord.ext import commands
from typing import Optional
from discord import app_commands
from cmd_common import common_commands as cc
from modules.permissions import has_permission
from modules.utility import handle_help_command
import globals
import os
import json
import random
# Retrieve primary guild info if needed (for logging or other purposes)
primary_guild = globals.constants.primary_discord_guild() # e.g., {"object": discord.Object(id=1234567890), "id": 1234567890}
def setup(bot):
"""
Attach commands to the Discord bot and register textual commands.
This function loads configuration data, then registers several commands:
- sync_commands: (OWNER ONLY) Manually syncs the bots command tree.
- available: Lists commands available to the user.
- help: Provides detailed help information for commands.
- greet: Makes the bot greet the user.
- ping: Checks the bot's uptime and latency.
- howl: Makes the bot attempt a howl.
- hi: A development command to test permission handling.
- quote: Interact with the quotes system (multiple subcommands supported).
All commands here are invoked with your text command prefix (e.g. "!")
"""
config_data = globals.load_config_file()
@bot.command(name='funfact', aliases=['fun-fact'])
async def funfact_command(ctx, *keywords):
# keywords is a tuple of strings from the command arguments.
fact = cc.get_fun_fact(list(keywords))
# Reply to the invoking user.
await ctx.reply(fact)
# -------------------------------------------------------------------------
# TEXTUAL COMMAND: quote
# -------------------------------------------------------------------------
@bot.command(name="quote")
async def cmd_quote_text(ctx, *, arg_str: str = ""):
"""
Handle the '!quote' command with multiple subcommands.
Usage:
- !quote
-> Retrieves a random (non-removed) quote.
- !quote <number>
-> Retrieves a specific quote by its ID.
- !quote add <quote text>
-> Adds a new quote and replies with its quote number.
- !quote remove <number>
-> Removes the specified quote.
- !quote restore <number>
-> Restores a previously removed quote.
- !quote info <number>
-> Displays stored information about the quote.
- !quote search [keywords]
-> Searches for the best matching quote.
- !quote latest
-> Retrieves the latest (most recent) non-removed quote.
"""
if not globals.init_db_conn:
await ctx.reply("Database is unavailable, sorry.")
return
args = arg_str.split() if arg_str else []
globals.log(f"'quote' command initiated with arguments: {args}", "DEBUG")
result = await cc.handle_quote_command(
db_conn=globals.init_db_conn,
is_discord=True,
ctx=ctx,
args=args,
game_name=None
)
globals.log(f"'quote' result: {result}", "DEBUG")
if hasattr(result, "to_dict"):
await ctx.reply(embed=result)
else:
await ctx.reply(result)
# -------------------------------------------------------------------------
# TEXTUAL COMMAND: howl
# -------------------------------------------------------------------------
@bot.command(name="howl")
async def cmd_howl_text(ctx):
"""
Handle the '!howl' command.
Usage:
- !howl
-> Attempts a howl.
- !howl stat <user>
-> Looks up howling stats for a user (defaults to self if not provided).
"""
result = cc.handle_howl_command(ctx)
await ctx.reply(result)
# -------------------------------------------------------------------------
# TEXTUAL COMMAND: help
# -------------------------------------------------------------------------
@bot.command(name="help")
async def cmd_help_text(ctx, *, command: str = ""):
"""
Get help information about commands.
Usage:
- !help
-> Provides a list of all commands with brief descriptions.
- !help <command>
-> Provides detailed help information for the specified command.
"""
result = await handle_help_command(ctx, command, bot, is_discord=True)
await ctx.reply(result)
# -------------------------------------------------------------------------
# SLASH COMMAND: help
# -------------------------------------------------------------------------
@bot.tree.command(name="help", description="Get information about commands", guild=primary_guild["object"])
@app_commands.describe(command="The command to get help info about. Defaults to 'help'")
async def cmd_help_slash(interaction: discord.Interaction, command: Optional[str] = ""):
result = await handle_help_command(interaction, command, bot, is_discord=True)
await interaction.response.send_message(result)
# -------------------------------------------------------------------------
# TEXTUAL COMMAND: ping
# -------------------------------------------------------------------------
@bot.command(name="ping")
async def cmd_ping_text(ctx):
"""
Check the bot's uptime and latency.
Usage:
- !ping
-> Returns the bot's uptime along with its latency in milliseconds.
"""
result = cc.ping()
latency = round(float(bot.latency) * 1000)
result += f" (*latency: {latency}ms*)"
await ctx.reply(result)
# -------------------------------------------------------------------------
# Final logging: list registered commands.
# -------------------------------------------------------------------------
try:
command_names = [cmd.name for cmd in bot.commands] # Extract command names
globals.log(f"Registering commands for Discord: {command_names}", "DEBUG")
except Exception as e:
globals.log(f"An error occurred while printing registered commands for Discord: {e}", "WARNING")

19
cmd_discord/__init__.py Normal file
View File

@ -0,0 +1,19 @@
# cmd_discord/__init__.py
import os
import importlib
def setup(bot):
"""
Dynamically load all commands from the cmd_discord folder.
"""
# Get a list of all command files (excluding __init__.py)
command_files = [
f.replace('.py', '') for f in os.listdir(os.path.dirname(__file__))
if f.endswith('.py') and f != '__init__.py'
]
# Import and set up each command module
for command in command_files:
module = importlib.import_module(f".{command}", package='cmd_discord')
if hasattr(module, 'setup'):
module.setup(bot)

14
cmd_discord/funfact.py Normal file
View File

@ -0,0 +1,14 @@
# cmd_discord/howl.py
from discord.ext import commands
import cmd_common.common_commands as cc
def setup(bot):
"""
Registers the '!howl' command for Discord.
"""
@bot.command(name='funfact', aliases=['fun-fact'])
async def funfact_command(ctx, *keywords):
# keywords is a tuple of strings from the command arguments.
fact = cc.get_fun_fact(list(keywords))
# Reply to the invoking user.
await ctx.reply(fact)

37
cmd_discord/help.py Normal file
View File

@ -0,0 +1,37 @@
# cmd_discord/howl.py
from discord.ext import commands
import discord
from discord import app_commands
from typing import Optional
import cmd_common.common_commands as cc
import globals
# Retrieve primary guild info if needed (for logging or other purposes)
primary_guild = globals.constants.primary_discord_guild() # e.g., {"object": discord.Object(id=1234567890), "id": 1234567890}
def setup(bot):
"""
Registers the '!help' command for Discord.
"""
@bot.command(name="help")
async def cmd_help_text(ctx, *, command: str = ""):
"""
Get help information about commands.
Usage:
- !help
-> Provides a list of all commands with brief descriptions.
- !help <command>
-> Provides detailed help information for the specified command.
"""
result = await cc.handle_help_command(ctx, command, bot, is_discord=True)
await ctx.reply(result)
# -------------------------------------------------------------------------
# SLASH COMMAND: help
# -------------------------------------------------------------------------
@bot.tree.command(name="help", description="Get information about commands", guild=primary_guild["object"])
@app_commands.describe(command="The command to get help info about. Defaults to 'help'")
async def cmd_help_slash(interaction: discord.Interaction, command: Optional[str] = ""):
result = await cc.handle_help_command(interaction, command, bot, is_discord=True)
await interaction.response.send_message(result)

18
cmd_discord/howl.py Normal file
View File

@ -0,0 +1,18 @@
# cmd_discord/howl.py
from discord.ext import commands
import cmd_common.common_commands as cc
def setup(bot):
"""
Registers the '!howl' command for Discord.
"""
@bot.command(name="howl")
async def cmd_howl_text(ctx):
"""
Handle the '!howl' command.
Usage:
- !howl -> Attempts a howl.
- !howl stat <user> -> Looks up howling stats for a user.
"""
result = cc.handle_howl_command(ctx)
await ctx.reply(result)

21
cmd_discord/ping.py Normal file
View File

@ -0,0 +1,21 @@
# cmd_discord/howl.py
from discord.ext import commands
import cmd_common.common_commands as cc
def setup(bot):
"""
Registers the '!ping' command for Discord.
"""
@bot.command(name="ping")
async def cmd_ping_text(ctx):
"""
Check the bot's uptime and latency.
Usage:
- !ping
-> Returns the bot's uptime along with its latency in milliseconds.
"""
result = cc.ping()
latency = round(float(bot.latency) * 1000)
result += f" (*latency: {latency}ms*)"
await ctx.reply(result)

50
cmd_discord/quote.py Normal file
View File

@ -0,0 +1,50 @@
# cmd_discord/howl.py
from discord.ext import commands
import globals
import cmd_common.common_commands as cc
def setup(bot):
"""
Registers the '!quote' command for Discord.
"""
@bot.command(name="quote")
async def cmd_quote_text(ctx, *, arg_str: str = ""):
"""
Handle the '!quote' command with multiple subcommands.
Usage:
- !quote
-> Retrieves a random (non-removed) quote.
- !quote <number>
-> Retrieves a specific quote by its ID.
- !quote add <quote text>
-> Adds a new quote and replies with its quote number.
- !quote remove <number>
-> Removes the specified quote.
- !quote restore <number>
-> Restores a previously removed quote.
- !quote info <number>
-> Displays stored information about the quote.
- !quote search [keywords]
-> Searches for the best matching quote.
- !quote latest
-> Retrieves the latest (most recent) non-removed quote.
"""
if not globals.init_db_conn:
await ctx.reply("Database is unavailable, sorry.")
return
args = arg_str.split() if arg_str else []
globals.log(f"'quote' command initiated with arguments: {args}", "DEBUG")
result = await cc.handle_quote_command(
db_conn=globals.init_db_conn,
is_discord=True,
ctx=ctx,
args=args,
game_name=None
)
globals.log(f"'quote' result: {result}", "DEBUG")
if hasattr(result, "to_dict"):
await ctx.reply(embed=result)
else:
await ctx.reply(result)

View File

@ -1,202 +0,0 @@
# cmd_twitch.py
from twitchio.ext import commands
import globals
from cmd_common import common_commands as cc
from modules.permissions import has_permission
from modules.utility import handle_help_command, is_channel_live, command_allowed_twitch, get_current_twitch_game
def setup(bot, db_conn=None):
"""
This function is called to load/attach commands to the `bot`.
We also attach the db_conn and log so the commands can use them.
"""
@bot.command(name='getgame')
@command_allowed_twitch
async def cmd_getgame(ctx: commands.Context):
channel_name = ctx.channel.name
game_name = await get_current_game(bot, channel_name)
await ctx.reply(game_name)
@bot.command(name='funfact', aliases=['fun-fact'])
@command_allowed_twitch
async def cmd_funfact(ctx: commands.Context, *keywords: str):
# Convert keywords tuple to list and pass to get_fun_fact.
fact = cc.get_fun_fact(list(keywords))
# Reply to the invoking user by prefixing their name.
await ctx.reply(fact)
@bot.command(name="greet")
@command_allowed_twitch
async def cmd_greet(ctx: commands.Context):
if not await is_channel_live():
result = cc.greet(ctx.author.display_name, "Twitch")
await ctx.reply(result)
@bot.command(name="ping")
@command_allowed_twitch
async def cmd_ping(ctx: commands.Context):
if not await is_channel_live():
result = cc.ping()
await ctx.reply(result)
@bot.command(name="howl")
@command_allowed_twitch
async def cmd_howl(ctx: commands.Context):
if not await is_channel_live():
response = cc.handle_howl_command(ctx)
await ctx.reply(response)
@bot.command(name="hi")
@command_allowed_twitch
async def cmd_hi(ctx: commands.Context):
if not await is_channel_live():
user_id = str(ctx.author.id) # Twitch user ID
user_roles = [role.lower() for role in ctx.author.badges.keys()] # "roles" from Twitch badges
if not has_permission("hi", user_id, user_roles, "twitch"):
return await ctx.send("You don't have permission to use this command.")
await ctx.reply("Hello there!")
# @bot.command(name="acc_link")
# @monitor_cmds(bot.log)
# async def cmd_acc_link(ctx, link_code: str):
# """Handles the Twitch command to link accounts."""
# from modules import db
# twitch_user_id = str(ctx.author.id)
# twitch_username = ctx.author.name
# # Check if the link code exists
# result = db.run_db_operation(
# bot.db_conn, "read",
# "SELECT DISCORD_USER_ID FROM link_codes WHERE LINK_CODE = ?", (link_code,),
# bot.log
# )
# if not result:
# await ctx.send("Invalid or expired link code. Please try again.")
# return
# discord_user_id = result[0][0]
# # Store the Twitch user info in the users table
# db.run_db_operation(
# bot.db_conn, "update",
# "UPDATE users SET twitch_user_id = ?, twitch_username = ?, datetime_linked = CURRENT_TIMESTAMP WHERE discord_user_id = ?",
# (twitch_user_id, twitch_username, discord_user_id), bot.log
# )
# # Remove the used link code
# db.run_db_operation(bot.db_conn, "write", "DELETE FROM link_codes WHERE LINK_CODE = ?", (link_code,), bot.log)
# # Notify the user
# await ctx.send(f"✅ Successfully linked Discord user **{discord_user_id}** with Twitch account **{twitch_username}**.")
# @bot.command(name="quote")
# async def cmd_quote(ctx: commands.Context):
# """
# Handles the !quote command with multiple subcommands.
# Usage:
# - !quote
# -> Retrieves a random (non-removed) quote.
# - !quote <number>
# -> Retrieves the specific quote by ID.
# - !quote add <quote text>
# -> Adds a new quote and replies with its quote number.
# - !quote remove <number>
# -> Removes the specified quote.
# - !quote info <number>
# -> Displays stored information about the quote (as an embed on Discord).
# - !quote search [keywords]
# -> Searches for the best matching quote based on the provided keywords.
# - !quote last/latest/newest
# -> Retrieves the latest (most recent) non-removed quote.
# """
# if not bot.db_conn:
# return await ctx.send("Database is unavailable, sorry.")
# parts = ctx.message.content.strip().split()
# args = parts[1:] if len(parts) > 1 else []
# def get_twitch_game_for_channel(chan_name):
# # Placeholder for your actual logic to fetch the current game
# return "SomeGame"
# result = await cc.handle_quote_command(
# db_conn=bot.db_conn,
# is_discord=False,
# ctx=ctx,
# args=args,
# get_twitch_game_for_channel=get_twitch_game_for_channel
# )
# await ctx.send(result)
@bot.command(name="quote")
@command_allowed_twitch
async def cmd_quote(ctx: commands.Context):
"""
Handles the !quote command with multiple subcommands.
Usage:
- !quote
-> Retrieves a random (non-removed) quote.
- !quote <number>
-> Retrieves the specific quote by ID.
- !quote add <quote text>
-> Adds a new quote and replies with its quote number.
- !quote remove <number>
-> Removes the specified quote.
- `!quote restore <number>`
-> Restores a previously removed quote.
- !quote info <number>
-> Displays stored information about the quote (as an embed on Discord).
- !quote search [keywords]
-> Searches for the best matching quote based on the provided keywords.
- !quote last/latest/newest
-> Retrieves the latest (most recent) non-removed quote.
"""
if not await is_channel_live():
if not globals.init_db_conn:
await ctx.reply("Database is unavailable, sorry.")
return
# Parse the arguments from the message text
args = ctx.message.content.strip().split()
args = args[1:] if args else []
globals.log(f"'quote' command initiated with arguments: {args}", "DEBUG")
globals.log(f"'quote' command message content: {ctx.message.content}", "DEBUG")
result = await cc.handle_quote_command(
db_conn=globals.init_db_conn,
is_discord=False,
ctx=ctx,
args=args,
game_name=await get_twitch_current_game(bot, ctx.channel.name)
)
globals.log(f"'quote' result: {result}", "DEBUG")
await ctx.reply(result)
@bot.command(name="help")
@command_allowed_twitch
async def cmd_help(ctx):
if not await is_channel_live(bot):
parts = ctx.message.content.strip().split()
cmd_name = parts[1] if len(parts) > 1 else None
await handle_help_command(ctx, cmd_name, bot, is_discord=False)
######################
# The following log entry must be last in the file to verify commands loading as they should
######################
# Debug: Print that commands are being registered
try:
globals.log(f"Registering commands for Twitch: {list(bot.commands.keys())}", "DEBUG")
except Exception as e:
globals.log(f"An error occured while printing registered commands for Twitch: {e}", "WARNING")

19
cmd_twitch/__init__.py Normal file
View File

@ -0,0 +1,19 @@
# cmd_twitch/__init__.py
import os
import importlib
def setup(bot):
"""
Dynamically load all commands from the cmd_twitch folder.
"""
# Get a list of all command files (excluding __init__.py)
command_files = [
f.replace('.py', '') for f in os.listdir(os.path.dirname(__file__))
if f.endswith('.py') and f != '__init__.py'
]
# Import and set up each command module
for command in command_files:
module = importlib.import_module(f".{command}", package='cmd_twitch')
if hasattr(module, 'setup'):
module.setup(bot)

19
cmd_twitch/funfact.py Normal file
View File

@ -0,0 +1,19 @@
# cmd_twitch/funfact.py
from twitchio.ext import commands
from cmd_common import common_commands as cc
def setup(bot):
"""
Registers the '!funfact' command for Twitch.
"""
@bot.command(name='funfact', aliases=['fun-fact'])
async def cmd_funfact(ctx: commands.Context, *keywords: str):
"""
Handle the '!funfact' command.
Usage:
- !funfact <keywords>
-> Displays a fun fact related to the given keywords.
"""
fact = cc.get_fun_fact(list(keywords))
await ctx.reply(fact)

19
cmd_twitch/getgame.py Normal file
View File

@ -0,0 +1,19 @@
# cmd_twitch/getgame.py
from twitchio.ext import commands
from modules.utility import get_current_twitch_game
def setup(bot):
"""
Registers the '!getgame' command for Twitch.
"""
@bot.command(name='getgame')
async def cmd_getgame(ctx: commands.Context):
"""
Retrieves the current game being played on Twitch.
Usage:
- !getgame -> Shows the current game for the channel.
"""
channel_name = ctx.channel.name
game_name = await get_current_twitch_game(bot, channel_name)
await ctx.reply(game_name)

21
cmd_twitch/help.py Normal file
View File

@ -0,0 +1,21 @@
# cmd_twitch/help.py
from twitchio.ext import commands
from modules.utility import handle_help_command, is_channel_live
def setup(bot):
"""
Registers the '!help' command for Twitch.
"""
@bot.command(name="help")
async def cmd_help(ctx):
"""
Displays help information for available commands.
Usage:
- !help -> Lists all commands.
- !help <command> -> Detailed help for a specific command.
"""
if not await is_channel_live(bot):
parts = ctx.message.content.strip().split()
cmd_name = parts[1] if len(parts) > 1 else None
await handle_help_command(ctx, cmd_name, bot, is_discord=False)

21
cmd_twitch/howl.py Normal file
View File

@ -0,0 +1,21 @@
# cmd_twitch/howl.py
from twitchio.ext import commands
from cmd_common import common_commands as cc
from modules.utility import is_channel_live
def setup(bot):
"""
Registers the '!howl' command for Twitch.
"""
@bot.command(name="howl")
async def cmd_howl(ctx: commands.Context):
"""
Handle the '!howl' command.
Usage:
- !howl -> Attempts a howl.
- !howl stat <user> -> Looks up howling stats for a user.
"""
if not await is_channel_live(bot):
response = cc.handle_howl_command(ctx)
await ctx.reply(response)

18
cmd_twitch/ping.py Normal file
View File

@ -0,0 +1,18 @@
# cmd_twitch/ping.py
from twitchio.ext import commands
from cmd_common import common_commands as cc
def setup(bot):
"""
Registers the '!ping' command for Twitch.
"""
@bot.command(name="ping")
async def cmd_ping(ctx: commands.Context):
"""
Checks the bot's uptime and latency.
Usage:
- !ping -> Returns the bot's uptime and latency.
"""
result = cc.ping()
await ctx.reply(result)

46
cmd_twitch/quote.py Normal file
View File

@ -0,0 +1,46 @@
# cmd_twitch/quote.py
from twitchio.ext import commands
from cmd_common import common_commands as cc
from modules.utility import is_channel_live
import globals
def setup(bot):
"""
Registers the '!quote' command for Twitch.
"""
@bot.command(name="quote")
async def cmd_quote(ctx: commands.Context):
"""
Handles the !quote command with multiple subcommands.
Usage:
- !quote
-> Retrieves a random (non-removed) quote.
- !quote <number>
-> Retrieves the specific quote by ID.
- !quote add <quote text>
-> Adds a new quote and replies with its quote number.
- !quote remove <number>
-> Removes the specified quote.
- !quote restore <number>
-> Restores a previously removed quote.
- !quote info <number>
-> Displays stored information about the quote.
- !quote search [keywords]
-> Searches for the best matching quote.
- !quote last/latest/newest
-> Retrieves the latest (most recent) non-removed quote.
"""
if not await is_channel_live(bot):
if not globals.init_db_conn:
await ctx.reply("Database is unavailable, sorry.")
return
args = ctx.message.content.strip().split()[1:]
result = await cc.handle_quote_command(
db_conn=globals.init_db_conn,
is_discord=False,
ctx=ctx,
args=args
)
await ctx.reply(result)

View File

@ -0,0 +1,26 @@
{
"notes_on_activity_mode": {
"0": "Disable bot activites",
"1": "Static activity",
"2": "Rotating activity",
"3": "Dynamic activity"
},
"activity_mode": 2,
"static_activity": {
"type": "Custom",
"name": "Listening for howls!"
},
"rotating_activities": [
{ "type": "Listening", "name": "howls" },
{ "type": "Playing", "name": "with my commands" },
{ "type": "Watching", "name": "Twitch streams" },
{ "type": "Watching", "name": "Kami code" },
{ "type": "Custom", "name": "I AM EVOLVING!"},
{ "type": "Watching", "name": "the Elder do their thing"}
],
"dynamic_activities": {
"twitch_live": { "type": "Streaming", "name": "OokamiKunTV", "url": "https://twitch.tv/OokamiKunTV" },
"default_idle": { "type": "Playing", "name": "around in Discord" }
},
"rotation_interval": 300
}