···99## Pre-reqs
10101111- Python 3.12
1212-- Poetry
1212+- UV
13131414## Environment Setup
15151616-`poetry install`
1616+`uv sync`
17171818### .env
19192020Minimal needed is an env var called `TOKEN` that contains the bot's token.
21212222-Cogs may have additional options, these variables **should** be prefix with the cog's name
2222+Cogs may have additional options, these variables **should** be prefix with the
2323+cog's name
23242425ex: Cog `Math` may have `Math.TEMP_UNIT=fahrenheit`.
2526···27282829### [Markov](src/bingus/cogs/markov.py)
29303030-A markov chain is an incredibly simple model where it decides the next token based on previous
3131-knowledge of what tokens the current token has been proceeded by.
3131+A markov chain is an incredibly simple model where it decides the next token
3232+based on previous knowledge of what tokens the current token has been proceeded by.
32333334#### Markov Commands
3435···3738- `/markov`: Make bingus try and reply to a prompt passed, use this to bypass the 80% change that bingus
3839 usually has to reply
3940- `/scan_history`*: Scan the history of the current channel and add it to the chain. Since Bingus only learns
4040- from *new* messages while he's active, you may need to do this when restarting him. This command can take a while depending on the number of messages.
4141-- `/dump_chain`*: Dumps the entire underlying markov chain as a JSON file and sends it
4242-- `/load_chain`*: Loads a markov chain JSON (as generated by `/dump_chain`) additively
4141+ from *new\* messages while he's active, you may need to do this when restarting him. This command can take a while depending on the number of messages.
4242+- `/dump_chain`\*: Dumps the entire underlying markov chain as a JSON file and sends it
4343+- `/load_chain`\*: Loads a markov chain JSON (as generated by `/dump_chain`) additively
4344- `/weights`: Dump the weights of the specified token to other tokens
44454546#### Markov Config
46474747-- `Markov.REPLY_CHANNELS`: A *comma-delimited* list of channel IDs that the bot should have
4848+- `Markov.REPLY_CHANNELS`: A _comma-delimited_ list of channel IDs that the bot should have
4849 have a chance to reply to messages in. The bot still learns from all channels in realtime, but
4950 these channels it'll have an 80% of replying to any message
5151+5252+- `Markov.BRAIN_FILE`: Path to file where the chain will be persisted. This file will automatically be created
5353+ if it doesn't exist already. The file itself is msgpack, so it's recommended to give it a `msgpack` extension.
5454+ By default it will be set to `$PWD/brain.msgpack`
50555156## Adding Cogs
5257
···11-import discord
11+# import discord
22from discord.ext import commands
33-from discord.message import Message
33+# from discord.message import Message
445566class __CName__(commands.Cog):
77-87 # Setup any state for the cog here, this will
98 # exist for the run of the program
109 # If you need to change the parameters for __init__
+24-14
src/bingus/lib/markov.py
···11from dataclasses import dataclass
22import random
33-import json
43from typing import Optional
44+from pathlib import Path
55+from msgpack import packb, unpackb
566778@dataclass
···20212122@dataclass
2223class End:
2323-2424 def __str__(self):
2525 return "~END"
2626···3535 match t:
3636 case Word(w):
3737 return f"W-{w}"
3838- case End:
3939- return f"E--"
3838+ case _:
3939+ return "E--"
404041414242def token_de(s: str) -> Token:
···153153 def _chain_tokens(
154154 self, starting_token: Optional[Token] = None, max_length: int = 20
155155 ) -> list[Token]:
156156-157156 tokens = []
158157159158 if starting_token is None:
···192191 else:
193192 return self._chain(None, max_length=max_length)
194193195195- def dump(self) -> str:
196196- return json.dumps(
197197- {
198198- token_ser(e): {token_ser(k): v for k, v in w.to_tokens.items()}
199199- for e, w in self.edges.items()
200200- }
201201- )
194194+ def save_to_file(self, path: Path):
195195+ if not path.parent.exists():
196196+ path.parent.mkdir(parents=True)
197197+ path.write_bytes(self.dumpb())
198198+199199+ def load_from_file(path: Path):
200200+ return MarkovChain.loadb(path.read_bytes())
201201+202202+ def dumpb(self):
203203+ return packb(self.ser())
204204+205205+ def loadb(dat):
206206+ return MarkovChain.deser(unpackb(dat))
207207+208208+ def ser(self):
209209+ return {
210210+ token_ser(e): {token_ser(k): v for k, v in w.to_tokens.items()}
211211+ for e, w in self.edges.items()
212212+ }
202213203203- def load(source: str):
204204- dat = json.loads(source)
214214+ def deser(dat):
205215 edges = {
206216 token_de(e): StateTransitions({token_de(k): v for k, v in w.items()})
207217 for e, w in dat.items()
-1
src/bingus/lib/permissions.py
···223344class NotOwnerError(discord.ApplicationCommandError):
55-65 def __init__(self) -> None:
76 super().__init__("You are not allowed to run this command")
87