Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
36a98b9
Start on the fundemental rewrite of factoids. 2.5/17 commands done
ajax146 Jun 17, 2026
acdfe7e
Migrate factoid all
ajax146 Jun 17, 2026
1be8f9b
Do some current changelog features
ajax146 Jun 17, 2026
7af3a03
Add comment
ajax146 Jun 17, 2026
36e235e
Add a few more todos
ajax146 Jun 17, 2026
1dc562d
Factoid dealias, caching, rename add->create
ajax146 Jun 17, 2026
56dfe9e
Logger, IRC, name/message validation
ajax146 Jun 17, 2026
0cc4e7d
Add some new buttons, change the view around
ajax146 Jun 17, 2026
d7dffe3
Delete command, layout changes
ajax146 Jun 17, 2026
3147d71
Factoid edit command
ajax146 Jun 17, 2026
bf67098
Factoid flush command
ajax146 Jun 17, 2026
cff7e7c
Info and json commands
ajax146 Jun 17, 2026
26868c8
Start of loop system, loop create/delete
ajax146 Jun 18, 2026
0c3ff77
Loop all command, edit time, proper job deletion
ajax146 Jun 18, 2026
f484e5f
Loop refresh command, other changes
ajax146 Jun 18, 2026
880bdd8
Layout changes, loop edit, property commands
ajax146 Jun 18, 2026
61ff41e
Some code de-duplication
ajax146 Jun 18, 2026
a6b56f4
Add more todos
ajax146 Jun 18, 2026
588820a
Todo changes
ajax146 Jun 18, 2026
ce11255
Search/top commands done
ajax146 Jun 19, 2026
e625953
Handle a bunch of todo tasks
ajax146 Jun 19, 2026
d0290da
Handle some more todos
ajax146 Jun 19, 2026
8a28402
A bit more progress
ajax146 Jun 20, 2026
003d46f
More progress, fix bugs, cleanup structure
ajax146 Jun 20, 2026
e6bb193
Fix a bunch of bugs, handle a bunch of TODO, clean up the code a bit
ajax146 Jun 20, 2026
6a065b0
Fix a few bugs, other cleanup
ajax146 Jun 21, 2026
b91790e
Fix some bugs, add some more TODOs
ajax146 Jul 7, 2026
af152e1
Formatting changes part 1
ajax146 Aug 21, 2026
1df7139
Formatting part 2
ajax146 Aug 21, 2026
6502799
Formatting part 3
ajax146 Aug 21, 2026
230d6f3
Formatting 4
ajax146 Aug 21, 2026
ccc4e30
Formatting 5
ajax146 Aug 21, 2026
4a9e0e1
Final formatting
ajax146 Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ spelling-store-unknown-words=no
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
TODO,
BUG


[TYPECHECK]
Expand Down Expand Up @@ -289,7 +290,8 @@ indent-after-paren=4
indent-string=' '

# Maximum number of characters on a single line.
max-line-length=100
# Following the standards set forth by black, this will be removed
max-line-length=100000000

# Maximum number of lines in a module.
max-module-lines=1000
Expand Down
12 changes: 10 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,16 @@ Changes since 2026.06.15
## Operation

### Factoid
- Make /factoid call work with factoids with spaces
- Fix permissions on /factoid add
- Complete migration to application commands
- Factoids are now allowed to use spaces
- /factoid add was renamed to /factoid create
- Fix permissions on /factoid create
- /factoid all has been reworked, is now always ephemeral
- /factoid all will now filter to only callable factoids, hiding disabled factoids, and hiding restricted factoids if not in a restricted channel
- /factoid call now works respects threads and restricted factoids
- /factoid call now works with IRC
- /factoid call now shows a "I see nothing" and "Save to DMs" button on factoids
- /factoid dealias now shows the remaining aliases on success

### Relay
- Make relay only ping users with words starting with an @
Expand Down
1 change: 1 addition & 0 deletions configuration/config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"embed_embed_roles": [],
"factoids_admin_roles": [],
"factoids_disable_embeds": false,
"factoids_disable_prefix": true,
"factoids_manage_roles": [],
"factoids_prefix": "?",
"factoids_restricted_list": [],
Expand Down
4 changes: 4 additions & 0 deletions configuration/config.meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@
"datatype": "bool",
"description": "This will force all factoids to not use embeds."
},
"factoids_disable_prefix": {
"datatype": "bool",
"description": "Whether or not to disable calling factoids by a prefix command."
},
"factoids_manage_roles": {
"datatype": "list[discord.Role]",
"description": "The roles required to manage factoids"
Expand Down
104 changes: 69 additions & 35 deletions core/databases.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,57 +127,90 @@ class DuckUser(bot.db.Model):
)
speed_record: float = bot.db.Column(bot.db.Float, default=-1.0)

class Factoid(bot.db.Model):
"""The postgres table for factoids
Currently used in factoid.py
class FactoidData(bot.db.Model):
"""
FactoidData is the data of the factoid, content displayed and how its displayed

Attributes:
factoid_id (int): The primary key of the factoid
name (str): The name of the factoid
guild (str): The string guild ID for the guild that the factoid is in
message (str): The string message of the factoid
time (datetime.datetime): When the factoid was created NOT edited
embed_config (str): The json of the factoid
hidden (bool): If the factoid should be hidden or not
protected (bool): If the factoid should be protected
disabled (bool): If the factoid should be disabled
restricted (bool): If the factoid should be restricted
alias (str): The string representation of the parent
factoid_data_id (int): The data ID of the factoid
guild (str): The ID of the guild this factoid belongs to
message (str): The plaintext message of this factoid
create_time (datetime): The time this factoid was created in the database
edit_time (datetime): The last time this factoid was edited in any way
json_string (str): The JSON of an embed, parsed into a string
flags (int): A bitwise value of the property flags of this factoid
timed_called (int): The amount of times this factoid was called
"""

__tablename__ = "factoids"
__tablename__ = "factoid_data"

factoid_id: int = bot.db.Column(bot.db.Integer, primary_key=True)
name: str = bot.db.Column(bot.db.String)
guild: str = bot.db.Column(bot.db.String)
factoid_data_id: int = bot.db.Column(bot.db.Integer, primary_key=True)
guild: str = bot.db.Column(bot.db.String, index=True)
message: str = bot.db.Column(bot.db.String)
time: datetime.datetime = bot.db.Column(
create_time: datetime.datetime = bot.db.Column(
bot.db.DateTime, default=datetime.datetime.utcnow
)
embed_config: str = bot.db.Column(bot.db.String, default=None)
hidden: bool = bot.db.Column(bot.db.Boolean, default=False)
protected: bool = bot.db.Column(bot.db.Boolean, default=False)
disabled: bool = bot.db.Column(bot.db.Boolean, default=False)
restricted: bool = bot.db.Column(bot.db.Boolean, default=False)
alias: str = bot.db.Column(bot.db.String, default=None)
edit_time: datetime.datetime = bot.db.Column(
bot.db.DateTime,
default=datetime.datetime.utcnow,
)
json_string: str = bot.db.Column(bot.db.String, default=None)
flags: int = bot.db.Column(bot.db.Integer, default=0)
times_called: int = bot.db.Column(bot.db.Integer, default=0)

class FactoidCall(bot.db.Model):
"""
FactoidCall is the name of a factoid. All FactoidCall's are tied to a FactoidData

Attributes:
factoid_call_id (int): This is the ID of the call
guild (str): This is the ID of the guild this factoid belongs to
name (str): This is the name, used and displayed to users
factoid_data_id (object): The linked foreign ID for the related FactoidData
"""

__tablename__ = "factoid_calls"

__table_args__ = (
bot.db.UniqueConstraint(
"guild", "name", name="uq_factoid_calls_guild_name"
),
)

factoid_call_id: int = bot.db.Column(bot.db.Integer, primary_key=True)
guild: str = bot.db.Column(bot.db.String, index=True)
name: str = bot.db.Column(bot.db.String, index=True)

factoid_data_id = bot.db.Column(
bot.db.Integer,
bot.db.ForeignKey("factoid_data.factoid_data_id"),
nullable=False,
index=True,
)

class FactoidJob(bot.db.Model):
"""The postgres table for factoid loops
Currently used in factoid.py
"""
FactoidJob is a database entry that represents loop jobs for factoids

Attributes:
job_id (int): The primary key, ID of the job
factoid (int): The primary key of the linked factoid
channel (str): The channel this loop needs to run in
cron (str): The frequency this job should run
factoid_job_id (int): This is the ID of the job
guild (str): This is the ID of the guild this job belongs to
factoid_data_id (object): The linked foreign ID for the related FactoidData
channel (str): This is the ID of the channel the loop should execute in
cron (str): This is the cron syntax for the job
"""

__tablename__ = "factoid_jobs"

job_id: int = bot.db.Column(bot.db.Integer, primary_key=True)
factoid: int = bot.db.Column(
bot.db.Integer, bot.db.ForeignKey("factoids.factoid_id")
factoid_job_id: int = bot.db.Column(bot.db.Integer, primary_key=True)
guild: str = bot.db.Column(bot.db.String, index=True)
factoid_data_id = bot.db.Column(
bot.db.Integer,
bot.db.ForeignKey("factoid_data.factoid_data_id"),
nullable=False,
index=True,
)

channel: str = bot.db.Column(bot.db.String)
cron: str = bot.db.Column(bot.db.String)

Expand Down Expand Up @@ -387,7 +420,8 @@ class XP(bot.db.Model):
bot.models.AppBans = ApplicationBans
bot.models.ModLog = ModLog
bot.models.DuckUser = DuckUser
bot.models.Factoid = Factoid
bot.models.FactoidData = FactoidData
bot.models.FactoidCall = FactoidCall
bot.models.FactoidJob = FactoidJob
bot.models.Grab = Grab
bot.models.IRCChannelMapping = IRCChannelMapping
Expand Down
26 changes: 26 additions & 0 deletions ircrelay/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import discord
import irc.client

from modules.operation import factoids


def parse_irc_message(event: irc.client.Event) -> dict[str, str]:
"""This turns the irc.client.Event object into a dictionary
Expand Down Expand Up @@ -108,6 +110,30 @@ def core_sent_message_format(
return message_str


def factoid_format(factoid: factoids.FactoidView, author: discord.Member) -> str:
"""This formats a message, adds a permissions prefix, user prefix, and fixes new lines and
file attachements

Args:
message (discord.Message): The discord message object to format
content_override (str): If passed, this will changed the content of the message

Returns:
str: The string, with unlimited length, that is ready to be sent to IRC
"""
use_content = factoid.message
IRC_BOLD = ""
permissions_prefix = get_permissions_prefix_for_discord_user(member=author)
message_content = f"{use_content}"
if len(message_content.strip()) == 0:
return ""
message_str = f"{IRC_BOLD}[D]{IRC_BOLD} <{permissions_prefix}"
message_str += f"{author.display_name}> {message_content}"
message_str = message_str.replace("\n", " ")
message_str = message_str.strip()
return message_str


def format_discord_edit_message(message: discord.Message) -> str:
"""This modifies a formatted message to add a message edited flag

Expand Down
14 changes: 14 additions & 0 deletions ircrelay/relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import modules.operation
from ircrelay import formatting
from modules.operation import factoids


class IRCBot(irc.bot.SingleServerIRCBot):
Expand Down Expand Up @@ -270,6 +271,19 @@ def send_reaction_from_discord(
)
self.send_message_to_channel(channel=channel, message=formatted_message)

def send_factoid_from_discord(
self: Self, channel: str, factoid: factoids.FactoidView, author: discord.Member
) -> None:
"""This processes a factoid into a valid IRC message, attributed to the correct autho

Args:
channel (str): The IRC channel to send the message to
factoid (factoids.FactoidView): The factoid that was sent
author (discord.Member): The author who sent the message
"""
formatted_message = formatting.factoid_format(factoid=factoid, author=author)
self.send_message_to_channel(channel=channel, message=formatted_message)

def send_message_from_discord(
self: Self, message: discord.Message, channel: str, content_override: str = None
) -> None:
Expand Down
1 change: 1 addition & 0 deletions modules/operation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Modules designed for operations of the server"""

from .application import *
from .factoids import *
from .relay import *
from .xp import *
Loading
Loading