Compare commits
No commits in common. "master" and "v1.1.1" have entirely different histories.
12 changed files with 47 additions and 287 deletions
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,4 +1,12 @@
|
||||||
|
# python stuff
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.egg-info/
|
|
||||||
/.mypy_cache/
|
# venv stuff
|
||||||
/.venv/
|
bin/
|
||||||
|
include/
|
||||||
|
lib/
|
||||||
|
lib64
|
||||||
|
pyvenv.cfg
|
||||||
|
|
||||||
|
# mypy stuff
|
||||||
|
.mypy_cache/
|
||||||
|
|
|
||||||
32
CHANGELOG.md
32
CHANGELOG.md
|
|
@ -2,34 +2,6 @@
|
||||||
|
|
||||||
## Next version
|
## Next version
|
||||||
|
|
||||||
## 1.2.0 (2022-08-21)
|
|
||||||
|
|
||||||
- update websockets dependency
|
|
||||||
- switch to pyproject.toml style setuptools config
|
|
||||||
|
|
||||||
## 1.1.5 (2020-01-26)
|
|
||||||
|
|
||||||
- more stability (I think)
|
|
||||||
|
|
||||||
## 1.1.4 (2019-06-21)
|
|
||||||
|
|
||||||
- add docstrings to `Bot`
|
|
||||||
- change `KILL_REPLY` and `RESTART_REPLY` to be optional in `Bot`
|
|
||||||
- fix imports
|
|
||||||
- fix room firing incorrect event
|
|
||||||
- update echobot example to newest version
|
|
||||||
- update example gitignore to newest version
|
|
||||||
|
|
||||||
## 1.1.3 (2019-04-19)
|
|
||||||
|
|
||||||
- add timeout for creating ws connections
|
|
||||||
- fix config file not reloading when restarting bots
|
|
||||||
|
|
||||||
## 1.1.2 (2019-04-14)
|
|
||||||
|
|
||||||
- fix room authentication
|
|
||||||
- resolve to test yaboli more thoroughly before publishing a new version
|
|
||||||
|
|
||||||
## 1.1.1 (2019-04-14)
|
## 1.1.1 (2019-04-14)
|
||||||
|
|
||||||
- add database class for easier sqlite3 access
|
- add database class for easier sqlite3 access
|
||||||
|
|
@ -39,7 +11,7 @@
|
||||||
- change how config files are passed along
|
- change how config files are passed along
|
||||||
- change module system to support config file changes
|
- change module system to support config file changes
|
||||||
|
|
||||||
## 1.0.0 (2019-04-13)
|
# 1.0.0 (2019-04-13)
|
||||||
|
|
||||||
- add fancy argument parsing
|
- add fancy argument parsing
|
||||||
- add login and logout command to room
|
- add login and logout command to room
|
||||||
|
|
@ -51,9 +23,9 @@
|
||||||
|
|
||||||
## 0.2.0 (2019-04-12)
|
## 0.2.0 (2019-04-12)
|
||||||
|
|
||||||
|
- change config file format
|
||||||
- add `ALIASES` variable to `Bot`
|
- add `ALIASES` variable to `Bot`
|
||||||
- add `on_connected` function to `Client`
|
- add `on_connected` function to `Client`
|
||||||
- change config file format
|
|
||||||
|
|
||||||
## 0.1.0 (2019-04-12)
|
## 0.1.0 (2019-04-12)
|
||||||
|
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -12,7 +12,7 @@ Ensure that you have at least Python 3.7 installed.
|
||||||
|
|
||||||
To install yaboli or update your installation to the latest version, run:
|
To install yaboli or update your installation to the latest version, run:
|
||||||
```
|
```
|
||||||
$ pip install git+https://github.com/Garmelon/yaboli@v1.2.0
|
$ pip install git+https://github.com/Garmelon/yaboli@v1.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
The use of [venv](https://docs.python.org/3/library/venv.html) is recommended.
|
The use of [venv](https://docs.python.org/3/library/venv.html) is recommended.
|
||||||
|
|
@ -39,17 +39,7 @@ class EchoBot(yaboli.Bot):
|
||||||
await message.reply(args.raw)
|
await message.reply(args.raw)
|
||||||
```
|
```
|
||||||
|
|
||||||
The bot's nick, cookie file and default rooms are specified in a config file,
|
The bot's nick, cookie file and default rooms are specified in a config file.
|
||||||
like so:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[general]
|
|
||||||
nick = EchoBot
|
|
||||||
cookie_file = bot.cookie
|
|
||||||
|
|
||||||
[rooms]
|
|
||||||
test
|
|
||||||
```
|
|
||||||
|
|
||||||
The help command from the botrulez uses the `HELP_GENERAL` and `HELP_SPECIFIC`
|
The help command from the botrulez uses the `HELP_GENERAL` and `HELP_SPECIFIC`
|
||||||
fields.
|
fields.
|
||||||
|
|
@ -62,9 +52,6 @@ In the `cmd_echo` function, the echo command is implemented. In this case, the
|
||||||
bot replies to the message containing the command with the raw argument string,
|
bot replies to the message containing the command with the raw argument string,
|
||||||
i. e. the text between the end of the "!echo" and the end of the whole message.
|
i. e. the text between the end of the "!echo" and the end of the whole message.
|
||||||
|
|
||||||
The full version of this echobot can be found [in the
|
|
||||||
examples](examples/echo/).
|
|
||||||
|
|
||||||
## TODOs
|
## TODOs
|
||||||
|
|
||||||
- [ ] document yaboli (markdown files in a "docs" folder?)
|
- [ ] document yaboli (markdown files in a "docs" folder?)
|
||||||
|
|
|
||||||
5
examples/echo/.gitignore
vendored
5
examples/echo/.gitignore
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
# These files are ignored because they may contain sensitive information you
|
|
||||||
# wouldn't want in your repo. If you need to have a config file in your repo,
|
|
||||||
# store a bot.conf.default with default settings.
|
|
||||||
*.conf
|
|
||||||
*.cookie
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
[general]
|
[general]
|
||||||
nick = EchoBot
|
nick = EchoBot
|
||||||
cookie_file = bot.cookie
|
|
||||||
|
|
||||||
[rooms]
|
[rooms]
|
||||||
test
|
test
|
||||||
|
|
@ -8,14 +8,13 @@ class EchoBot(yaboli.Bot):
|
||||||
"!echo <text> – reply with exactly <text>",
|
"!echo <text> – reply with exactly <text>",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, config_file):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(config_file)
|
||||||
self.register_botrulez(kill=True)
|
self.register_botrulez(kill=True)
|
||||||
self.register_general("echo", self.cmd_echo)
|
self.register_general("echo", self.cmd_echo)
|
||||||
|
|
||||||
async def cmd_echo(self, room, message, args):
|
async def cmd_echo(self, room, message, args):
|
||||||
text = args.raw.strip() # ignoring leading and trailing whitespace
|
await message.reply(args.raw)
|
||||||
await message.reply(text)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ lib/
|
||||||
lib64
|
lib64
|
||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
|
|
||||||
# bot stuff
|
# config files
|
||||||
#
|
|
||||||
# These files are ignored because they may contain sensitive information you
|
|
||||||
# wouldn't want in your repo. If you need to have a config file in your repo,
|
|
||||||
# store a bot.conf.default with default settings.
|
|
||||||
*.conf
|
*.conf
|
||||||
*.cookie
|
cookie_jar
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
[build-system]
|
from setuptools import setup
|
||||||
requires = ["setuptools"]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
|
||||||
[project]
|
setup(
|
||||||
name = "yaboli"
|
name="yaboli",
|
||||||
version = "1.2.0"
|
version="1.1.1",
|
||||||
dependencies = [
|
packages=["yaboli"],
|
||||||
"websockets >=10.3, <11"
|
install_requires=["websockets==7.0"],
|
||||||
]
|
)
|
||||||
|
|
||||||
# When updating the version, also:
|
# When updating the version, also:
|
||||||
# - update the README.md installation instructions
|
# - update the README.md installation instructions
|
||||||
|
|
@ -17,7 +17,7 @@ from .session import *
|
||||||
from .util import *
|
from .util import *
|
||||||
|
|
||||||
__all__ = ["STYLE", "FORMAT", "DATE_FORMAT", "FORMATTER", "enable_logging",
|
__all__ = ["STYLE", "FORMAT", "DATE_FORMAT", "FORMATTER", "enable_logging",
|
||||||
"run", "run_modulebot"]
|
"run"]
|
||||||
|
|
||||||
__all__ += bot.__all__
|
__all__ += bot.__all__
|
||||||
__all__ += client.__all__
|
__all__ += client.__all__
|
||||||
|
|
@ -54,12 +54,12 @@ def run(
|
||||||
bot_constructor: BotConstructor,
|
bot_constructor: BotConstructor,
|
||||||
config_file: str = "bot.conf",
|
config_file: str = "bot.conf",
|
||||||
) -> None:
|
) -> None:
|
||||||
|
# Load the config file
|
||||||
|
config = configparser.ConfigParser(allow_no_value=True)
|
||||||
|
config.read(config_file)
|
||||||
|
|
||||||
async def _run() -> None:
|
async def _run() -> None:
|
||||||
while True:
|
while True:
|
||||||
# Load the config file
|
|
||||||
config = configparser.ConfigParser(allow_no_value=True)
|
|
||||||
config.read(config_file)
|
|
||||||
|
|
||||||
bot = bot_constructor(config, config_file)
|
bot = bot_constructor(config, config_file)
|
||||||
await bot.run()
|
await bot.run()
|
||||||
|
|
||||||
|
|
@ -70,12 +70,12 @@ def run_modulebot(
|
||||||
module_constructors: Dict[str, ModuleConstructor],
|
module_constructors: Dict[str, ModuleConstructor],
|
||||||
config_file: str = "bot.conf",
|
config_file: str = "bot.conf",
|
||||||
) -> None:
|
) -> None:
|
||||||
|
# Load the config file
|
||||||
|
config = configparser.ConfigParser(allow_no_value=True)
|
||||||
|
config.read(config_file)
|
||||||
|
|
||||||
async def _run() -> None:
|
async def _run() -> None:
|
||||||
while True:
|
while True:
|
||||||
# Load the config file
|
|
||||||
config = configparser.ConfigParser(allow_no_value=True)
|
|
||||||
config.read(config_file)
|
|
||||||
|
|
||||||
modulebot = modulebot_constructor(config, config_file,
|
modulebot = modulebot_constructor(config, config_file,
|
||||||
module_constructors)
|
module_constructors)
|
||||||
await modulebot.run()
|
await modulebot.run()
|
||||||
|
|
|
||||||
192
yaboli/bot.py
192
yaboli/bot.py
|
|
@ -14,43 +14,13 @@ logger = logging.getLogger(__name__)
|
||||||
__all__ = ["Bot", "BotConstructor"]
|
__all__ = ["Bot", "BotConstructor"]
|
||||||
|
|
||||||
class Bot(Client):
|
class Bot(Client):
|
||||||
"""
|
|
||||||
A Bot is a Client that responds to commands and uses a config file to
|
|
||||||
automatically set its nick and join rooms.
|
|
||||||
|
|
||||||
The config file is loaded as a ConfigParser by the run() or run_modulebot()
|
|
||||||
functions and has the following structure:
|
|
||||||
|
|
||||||
A "general" section which contains:
|
|
||||||
- nick - the default nick of the bot (set to the empty string if you don't
|
|
||||||
want to set a nick)
|
|
||||||
- cookie_file (optional) - the file the cookie should be saved in
|
|
||||||
|
|
||||||
A "rooms" section which contains a list of rooms that the bot should
|
|
||||||
automatically join. This section is optional if you overwrite started().
|
|
||||||
The room list should have the format "roomname" or "roomname = password".
|
|
||||||
|
|
||||||
A bot has the following attributes:
|
|
||||||
- ALIASES - list of alternate nicks the bot responds to (see
|
|
||||||
process_commands())
|
|
||||||
- PING_REPLY - used by cmd_ping()
|
|
||||||
- HELP_GENERAL - used by cmd_help_general()
|
|
||||||
- HELP_SPECIFIC - used by cmd_help_specific()
|
|
||||||
- KILL_REPLY - used by cmd_kill()
|
|
||||||
- RESTART_REPLY - used by cmd_restart()
|
|
||||||
- GENERAL_SECTION - the name of the "general" section in the config file
|
|
||||||
(see above) (default: "general")
|
|
||||||
- ROOMS_SECTION - the name of the "rooms" section in the config file (see
|
|
||||||
above) (default: "rooms")
|
|
||||||
"""
|
|
||||||
|
|
||||||
ALIASES: List[str] = []
|
ALIASES: List[str] = []
|
||||||
|
|
||||||
PING_REPLY: str = "Pong!"
|
PING_REPLY: str = "Pong!"
|
||||||
HELP_GENERAL: Optional[str] = None
|
HELP_GENERAL: Optional[str] = None
|
||||||
HELP_SPECIFIC: Optional[List[str]] = None
|
HELP_SPECIFIC: Optional[List[str]] = None
|
||||||
KILL_REPLY: Optional[str] = "/me dies"
|
KILL_REPLY: str = "/me dies"
|
||||||
RESTART_REPLY: Optional[str] = "/me restarts"
|
RESTART_REPLY: str = "/me restarts"
|
||||||
|
|
||||||
GENERAL_SECTION = "general"
|
GENERAL_SECTION = "general"
|
||||||
ROOMS_SECTION = "rooms"
|
ROOMS_SECTION = "rooms"
|
||||||
|
|
@ -80,26 +50,10 @@ class Bot(Client):
|
||||||
self.start_time = datetime.datetime.now()
|
self.start_time = datetime.datetime.now()
|
||||||
|
|
||||||
def save_config(self) -> None:
|
def save_config(self) -> None:
|
||||||
"""
|
|
||||||
Save the current state of self.config to the file passed in __init__ as
|
|
||||||
the config_file parameter.
|
|
||||||
|
|
||||||
Usually, this is the file that self.config was loaded from (if you use
|
|
||||||
run or run_modulebot).
|
|
||||||
"""
|
|
||||||
|
|
||||||
with open(self.config_file, "w") as f:
|
with open(self.config_file, "w") as f:
|
||||||
self.config.write(f)
|
self.config.write(f)
|
||||||
|
|
||||||
async def started(self) -> None:
|
async def started(self) -> None:
|
||||||
"""
|
|
||||||
This Client function is overwritten in order to join all the rooms
|
|
||||||
listed in the "rooms" section of self.config.
|
|
||||||
|
|
||||||
If you need to overwrite this function but want to keep the auto-join
|
|
||||||
functionality, make sure to await super().started().
|
|
||||||
"""
|
|
||||||
|
|
||||||
for room, password in self.config[self.ROOMS_SECTION].items():
|
for room, password in self.config[self.ROOMS_SECTION].items():
|
||||||
if password is None:
|
if password is None:
|
||||||
await self.join(room)
|
await self.join(room)
|
||||||
|
|
@ -109,12 +63,6 @@ class Bot(Client):
|
||||||
# Registering commands
|
# Registering commands
|
||||||
|
|
||||||
def register(self, command: Command) -> None:
|
def register(self, command: Command) -> None:
|
||||||
"""
|
|
||||||
Register a Command (from the yaboli.command submodule).
|
|
||||||
|
|
||||||
Usually, you don't have to call this function yourself.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self._commands.append(command)
|
self._commands.append(command)
|
||||||
|
|
||||||
def register_general(self,
|
def register_general(self,
|
||||||
|
|
@ -122,23 +70,6 @@ class Bot(Client):
|
||||||
cmdfunc: GeneralCommandFunction,
|
cmdfunc: GeneralCommandFunction,
|
||||||
args: bool = True
|
args: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Register a function as general bot command (i. e. no @mention of the
|
|
||||||
bot nick after the !command). This function will be called by
|
|
||||||
process_commands() when the bot encounters a matching command.
|
|
||||||
|
|
||||||
name - the name of the command (If you want your command to be !hello,
|
|
||||||
the name is "hello".)
|
|
||||||
|
|
||||||
cmdfunc - the function that is called with the Room, LiveMessage and
|
|
||||||
ArgumentData when the bot encounters a matching command
|
|
||||||
|
|
||||||
args - whether the command may have arguments (If set to False, the
|
|
||||||
ArgumentData's has_args() function must also return False for the
|
|
||||||
command function to be called. If set to True, all ArgumentData is
|
|
||||||
valid.)
|
|
||||||
"""
|
|
||||||
|
|
||||||
command = GeneralCommand(name, cmdfunc, args)
|
command = GeneralCommand(name, cmdfunc, args)
|
||||||
self.register(command)
|
self.register(command)
|
||||||
|
|
||||||
|
|
@ -147,21 +78,6 @@ class Bot(Client):
|
||||||
cmdfunc: SpecificCommandFunction,
|
cmdfunc: SpecificCommandFunction,
|
||||||
args: bool = True
|
args: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Register a function as specific bot command (i. e. @mention of the bot
|
|
||||||
nick after the !command is required). This function will be called by
|
|
||||||
process_commands() when the bot encounters a matching command.
|
|
||||||
|
|
||||||
name - the name of the command (see register_general() for an
|
|
||||||
explanation)
|
|
||||||
|
|
||||||
cmdfunc - the function that is called with the Room, LiveMessage and
|
|
||||||
SpecificArgumentData when the bot encounters a matching command
|
|
||||||
|
|
||||||
args - whether the command may have arguments (see register_general()
|
|
||||||
for an explanation)
|
|
||||||
"""
|
|
||||||
|
|
||||||
command = SpecificCommand(name, cmdfunc, args)
|
command = SpecificCommand(name, cmdfunc, args)
|
||||||
self.register(command)
|
self.register(command)
|
||||||
|
|
||||||
|
|
@ -172,13 +88,6 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
aliases: List[str] = []
|
aliases: List[str] = []
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
If the message contains a command, call all matching command functions
|
|
||||||
that were previously registered.
|
|
||||||
|
|
||||||
This function is usually called by the overwritten on_send() function.
|
|
||||||
"""
|
|
||||||
|
|
||||||
nicks = [room.session.nick] + aliases
|
nicks = [room.session.nick] + aliases
|
||||||
data = CommandData.from_string(message.content)
|
data = CommandData.from_string(message.content)
|
||||||
|
|
||||||
|
|
@ -188,31 +97,11 @@ class Bot(Client):
|
||||||
await command.run(room, message, nicks, data)
|
await command.run(room, message, nicks, data)
|
||||||
|
|
||||||
async def on_send(self, room: Room, message: LiveMessage) -> None:
|
async def on_send(self, room: Room, message: LiveMessage) -> None:
|
||||||
"""
|
|
||||||
This Client function is overwritten in order to automatically call
|
|
||||||
process_commands() with self.ALIASES.
|
|
||||||
|
|
||||||
If you need to overwrite this function, make sure to await
|
|
||||||
process_commands() with self.ALIASES somewhere in your function, or
|
|
||||||
await super().on_send().
|
|
||||||
"""
|
|
||||||
|
|
||||||
await self.process_commands(room, message, aliases=self.ALIASES)
|
await self.process_commands(room, message, aliases=self.ALIASES)
|
||||||
|
|
||||||
# Help util
|
# Help util
|
||||||
|
|
||||||
def format_help(self, room: Room, lines: List[str]) -> str:
|
def format_help(self, room: Room, lines: List[str]) -> str:
|
||||||
"""
|
|
||||||
Format a list of strings into a string, replacing certain placeholders
|
|
||||||
with the actual values.
|
|
||||||
|
|
||||||
This function uses the str.format() function to replace the following:
|
|
||||||
|
|
||||||
- {nick} - the bot's current nick
|
|
||||||
- {mention} - the bot's current nick, run through mention()
|
|
||||||
- {atmention} - the bot's current nick, run through atmention()
|
|
||||||
"""
|
|
||||||
|
|
||||||
text = "\n".join(lines)
|
text = "\n".join(lines)
|
||||||
params = {
|
params = {
|
||||||
"nick": room.session.nick,
|
"nick": room.session.nick,
|
||||||
|
|
@ -230,36 +119,6 @@ class Bot(Client):
|
||||||
kill: bool = False,
|
kill: bool = False,
|
||||||
restart: bool = False,
|
restart: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Register the commands necessary for the bot to conform to the botrulez
|
|
||||||
(https://github.com/jedevc/botrulez). Also includes a few optional
|
|
||||||
botrulez commands that are disabled by default.
|
|
||||||
|
|
||||||
- ping - register general and specific cmd_ping()
|
|
||||||
- help_ - register cmd_help_general() and cmd_help_specific()
|
|
||||||
- uptime - register specific cmd_uptime
|
|
||||||
- kill - register specific cmd_kill (disabled by default)
|
|
||||||
- uptime - register specific cmd_uptime (disabled by default)
|
|
||||||
|
|
||||||
All commands are registered with args=False.
|
|
||||||
|
|
||||||
If you want to implement your own versions of these commands, it is
|
|
||||||
recommended that you set the respective argument to False in your call
|
|
||||||
to register_botrulez(), overwrite the existing command functions or
|
|
||||||
create your own, and then register them manually.
|
|
||||||
|
|
||||||
For help, that might look something like this, if you've written a
|
|
||||||
custom specific help that takes extra arguments but are using the
|
|
||||||
botrulez general help:
|
|
||||||
|
|
||||||
self.register_botrulez(help_=False)
|
|
||||||
self.register_general("help", self.cmd_help_general, args=False)
|
|
||||||
self.register_specific("help", self.cmd_help_custom)
|
|
||||||
|
|
||||||
In case you're asking, the help_ parameter has an underscore at the end
|
|
||||||
so it doesn't overlap the help() function.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if ping:
|
if ping:
|
||||||
self.register_general("ping", self.cmd_ping, args=False)
|
self.register_general("ping", self.cmd_ping, args=False)
|
||||||
self.register_specific("ping", self.cmd_ping, args=False)
|
self.register_specific("ping", self.cmd_ping, args=False)
|
||||||
|
|
@ -285,10 +144,6 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: ArgumentData
|
args: ArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Reply with self.PING_REPLY.
|
|
||||||
"""
|
|
||||||
|
|
||||||
await message.reply(self.PING_REPLY)
|
await message.reply(self.PING_REPLY)
|
||||||
|
|
||||||
async def cmd_help_general(self,
|
async def cmd_help_general(self,
|
||||||
|
|
@ -296,10 +151,6 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: ArgumentData
|
args: ArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Reply with self.HELP_GENERAL, if it is not None. Uses format_help().
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.HELP_GENERAL is not None:
|
if self.HELP_GENERAL is not None:
|
||||||
await message.reply(self.format_help(room, [self.HELP_GENERAL]))
|
await message.reply(self.format_help(room, [self.HELP_GENERAL]))
|
||||||
|
|
||||||
|
|
@ -308,10 +159,6 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: SpecificArgumentData
|
args: SpecificArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Reply with self.HELP_SPECIFIC, if it is not None. Uses format_help().
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.HELP_SPECIFIC is not None:
|
if self.HELP_SPECIFIC is not None:
|
||||||
await message.reply(self.format_help(room, self.HELP_SPECIFIC))
|
await message.reply(self.format_help(room, self.HELP_SPECIFIC))
|
||||||
|
|
||||||
|
|
@ -320,15 +167,6 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: SpecificArgumentData
|
args: SpecificArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Reply with the bot's uptime in the format specified by the botrulez.
|
|
||||||
|
|
||||||
This uses the time that the Bot was first started, not the time the
|
|
||||||
respective Room was created. A !restart (see register_botrulez()) will
|
|
||||||
reset the bot uptime, but leaving and re-joining a room or losing
|
|
||||||
connection won't.
|
|
||||||
"""
|
|
||||||
|
|
||||||
time = format_time(self.start_time)
|
time = format_time(self.start_time)
|
||||||
delta = format_delta(datetime.datetime.now() - self.start_time)
|
delta = format_delta(datetime.datetime.now() - self.start_time)
|
||||||
text = f"/me has been up since {time} UTC ({delta})"
|
text = f"/me has been up since {time} UTC ({delta})"
|
||||||
|
|
@ -339,18 +177,8 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: SpecificArgumentData
|
args: SpecificArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Remove the bot from this room.
|
|
||||||
|
|
||||||
If self.KILL_REPLY is not None, replies with that before leaving the
|
|
||||||
room.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.info(f"Killed in &{room.name} by {message.sender.atmention}")
|
logger.info(f"Killed in &{room.name} by {message.sender.atmention}")
|
||||||
|
await message.reply(self.KILL_REPLY)
|
||||||
if self.KILL_REPLY is not None:
|
|
||||||
await message.reply(self.KILL_REPLY)
|
|
||||||
|
|
||||||
await self.part(room)
|
await self.part(room)
|
||||||
|
|
||||||
async def cmd_restart(self,
|
async def cmd_restart(self,
|
||||||
|
|
@ -358,20 +186,8 @@ class Bot(Client):
|
||||||
message: LiveMessage,
|
message: LiveMessage,
|
||||||
args: SpecificArgumentData
|
args: SpecificArgumentData
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
|
||||||
Restart the whole Bot.
|
|
||||||
|
|
||||||
This is done by stopping the Bot, since the run() or run_modulebot()
|
|
||||||
functions start the Bot in a while True loop.
|
|
||||||
|
|
||||||
If self.RESTART_REPLY is not None, replies with that before restarting.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logger.info(f"Restarted in &{room.name} by {message.sender.atmention}")
|
logger.info(f"Restarted in &{room.name} by {message.sender.atmention}")
|
||||||
|
await message.reply(self.RESTART_REPLY)
|
||||||
if self.RESTART_REPLY is not None:
|
|
||||||
await message.reply(self.RESTART_REPLY)
|
|
||||||
|
|
||||||
await self.stop()
|
await self.stop()
|
||||||
|
|
||||||
BotConstructor = Callable[[configparser.ConfigParser, str], Bot]
|
BotConstructor = Callable[[configparser.ConfigParser, str], Bot]
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,6 @@ class Connection:
|
||||||
"part-event" and "ping".
|
"part-event" and "ping".
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Timeout for waiting for the ws connection to be established
|
|
||||||
CONNECT_TIMEOUT = 10 # seconds
|
|
||||||
|
|
||||||
# Maximum duration between euphoria's ping messages. Euphoria usually sends
|
# Maximum duration between euphoria's ping messages. Euphoria usually sends
|
||||||
# ping messages every 20 to 30 seconds.
|
# ping messages every 20 to 30 seconds.
|
||||||
PING_TIMEOUT = 40 # seconds
|
PING_TIMEOUT = 40 # seconds
|
||||||
|
|
@ -186,12 +183,8 @@ class Connection:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.debug(f"Creating ws connection to {self._url!r}")
|
logger.debug(f"Creating ws connection to {self._url!r}")
|
||||||
ws = await asyncio.wait_for(
|
ws = await websockets.connect(self._url,
|
||||||
websockets.connect(self._url,
|
extra_headers=self._cookie_jar.get_cookies_as_headers())
|
||||||
extra_headers=self._cookie_jar.get_cookies_as_headers()),
|
|
||||||
self.CONNECT_TIMEOUT
|
|
||||||
)
|
|
||||||
logger.debug(f"Established ws connection to {self._url!r}")
|
|
||||||
|
|
||||||
self._ws = ws
|
self._ws = ws
|
||||||
self._awaiting_replies = {}
|
self._awaiting_replies = {}
|
||||||
|
|
@ -207,7 +200,7 @@ class Connection:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except (websockets.InvalidHandshake, websockets.InvalidStatusCode,
|
except (websockets.InvalidHandshake, websockets.InvalidStatusCode,
|
||||||
OSError, asyncio.TimeoutError):
|
socket.gaierror):
|
||||||
logger.debug("Connection failed")
|
logger.debug("Connection failed")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -180,10 +180,10 @@ class Room:
|
||||||
if nick is not None and self._session is not None:
|
if nick is not None and self._session is not None:
|
||||||
self._session = self.session.with_nick(nick)
|
self._session = self.session.with_nick(nick)
|
||||||
|
|
||||||
# Send "snapshot" event
|
# Send "session" event
|
||||||
messages = [LiveMessage.from_data(self, msg_data)
|
messages = [LiveMessage.from_data(self, msg_data)
|
||||||
for msg_data in data["log"]]
|
for msg_data in data["log"]]
|
||||||
self._events.fire("snapshot", messages)
|
self._events.fire("session", messages)
|
||||||
|
|
||||||
self._snapshot_received = True
|
self._snapshot_received = True
|
||||||
await self._try_set_connected()
|
await self._try_set_connected()
|
||||||
|
|
@ -191,11 +191,8 @@ class Room:
|
||||||
async def _on_bounce_event(self, packet: Any) -> None:
|
async def _on_bounce_event(self, packet: Any) -> None:
|
||||||
data = packet["data"]
|
data = packet["data"]
|
||||||
|
|
||||||
# Can we even authenticate? (Assuming that passcode authentication is
|
# Can we even authenticate?
|
||||||
# available if no authentication options are given: Euphoria doesn't
|
if not "passcode" in data.get("auth_options", []):
|
||||||
# (always) send authentication options, even when passcode
|
|
||||||
# authentication works.)
|
|
||||||
if not "passcode" in data.get("auth_options", ["passcode"]):
|
|
||||||
self._set_connected_failed()
|
self._set_connected_failed()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue