Compare commits

...

17 Commits

Author SHA1 Message Date
Mal Hancock 004632537b Set theme jekyll-theme-minimal 2022-01-09 22:29:47 -08:00
mallory d528213c5f fix for listeners 2020-12-18 09:48:21 -08:00
mallory c3613f196a change message logic 2020-12-18 09:35:37 -08:00
mallory fdca6da624 changes to allow processing other events 2020-12-18 08:43:54 -08:00
Mal Hancock 5c9278bc63
Update __version__.py 2020-11-12 09:02:55 -08:00
Lars Kellogg-Stedman 35f5bb684b
correct UnboundLocalError in read_conf (#76)
When encountering an error, read_conf uses click.echo(...) to emit an
error message but then continue execution, causing the read_conf
method to throw an error of the form:

    UnboundLocalError: local variable 'output' referenced before assignment

By raising click.ClickException instead of calling click.echo, we
ensure that pinhook exits with an error message (and no traceback).
2020-11-12 08:10:31 -08:00
mallory 7c62e59c3a fix issue with reloading plugins in some contexts 2020-09-23 11:25:15 -07:00
mallory b995b866ec do not duplicate output function 2020-09-23 11:17:59 -07:00
Mal Hancock 9f62c6c67e
use built-in setup.py upload 2020-05-29 09:50:04 -07:00
Mal Hancock 855413a093
Create python-publish.yml 2020-05-29 09:25:06 -07:00
Mal Hancock cea5e6f855
Update FUNDING.yml 2020-05-29 09:19:04 -07:00
Mal Hancock e7b7a5b832
Update FUNDING.yml 2020-05-29 09:16:44 -07:00
Mal Hancock 0417dddf2f
Create FUNDING.yml 2020-05-29 08:37:20 -07:00
Mal Hancock a0378e09c9
version 1.9.3 (#74)
* bring TwitchBot up to date

* prevent responses from bot nick
2020-05-28 14:51:56 -07:00
Mal Hancock 5b241eee13
change example plugins to match current standards 2020-04-28 13:10:43 -07:00
Mal Hancock 0e510cc8b6
change example plugins to match current standards 2020-04-28 13:10:17 -07:00
Mallory Hancock 1c4fdb8d9e update readme to use updated decorators 2020-01-31 12:03:46 -08:00
9 changed files with 95 additions and 55 deletions

4
.github/FUNDING.yml vendored 100644
View File

@ -0,0 +1,4 @@
github: archangelic
ko_fi: archangelic
liberapay: archangelic
custom: ["https://paypal.me/archangelic", "https://cash.app/$archangelic"]

View File

@ -0,0 +1,30 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py upload

View File

@ -127,14 +127,14 @@ These options are the same for both IRC and Twitch
There are two types of plugins, commands and listeners. Commands only activate if a message starts with the command word, while listeners receive all messages and are parsed by the plugin for maximum flexibility.
In your chosen plugins directory ("plugins" by default) make a python file with a function. You use the `@pinhook.plugin.register` decorator to create command plugins, or `@pinhook.plugin.listener` to create listeners.
In your chosen plugins directory ("plugins" by default) make a python file with a function. You use the `@pinhook.plugin.command` decorator to create command plugins, or `@pinhook.plugin.listener` to create listeners.
The function will need to be structured as such:
```python
import pinhook.plugin
@pinhook.plugin.register('!test')
@pinhook.plugin.command('!test')
def test_plugin(msg):
message = '{}: this is a test!'.format(msg.nick)
return pinhook.plugin.message(message)
@ -162,15 +162,12 @@ It also contains the following IRC functions:
* `action`: same as privmsg, but does a CTCP action. (i.e., `/me does a thing`)
* `notice`: send a notice
You can optionally use the `@pinhook.plugin.ops` decorator to denote that a command should only be executable by a bot op.
* If you specify the optional second argument, it will be displayed when a non-op attempts to execute the command
You can optionally set a command to be used only by ops
The function will need to be structured as such:
```python
@pinhook.plugin.register('!test')
@pinhook.plugin.ops('!test', 'Only ops can run this command!')
@pinhook.plugin.command('!test', ops=True, ops_msg='This command can only be run by an op')
def test_plugin(msg):
return pinhook.plugin.message('This was run by an op!')
```

View File

@ -1 +1 @@
theme: jekyll-theme-midnight
theme: jekyll-theme-minimal

View File

@ -17,7 +17,7 @@ def build_output(rolls, modifier):
output = start
return output
@pinhook.plugin.register('!roll')
@pinhook.plugin.command('!roll')
def roll(msg):
matches = dicepattern.match(msg.arg)
if matches:

View File

@ -17,7 +17,7 @@ def build_output(rolls, modifier):
output = start
return output
@pinhook.plugin.register('!roll')
@pinhook.plugin.command('!roll')
def roll(msg):
matches = dicepattern.match(msg.arg)
if matches:

View File

@ -1 +1 @@
__version__ = '1.9.2'
__version__ = '1.9.7'

View File

@ -14,6 +14,20 @@ irc.client.ServerConnection.buffer_class.errors = 'replace'
class Bot(irc.bot.SingleServerIRCBot):
internal_commands = {
'join': 'join a channel',
'quit': 'force the bot to quit',
'reload': 'force bot to reload all plugins',
'enable': 'enable a plugin',
'disable': 'disable a plugin',
'op': 'add a user as bot operator',
'deop': 'remove a user as bot operator',
'ops': 'list all ops',
'ban': 'ban a user from using the bot',
'unban': 'remove bot ban for user',
'banlist': 'currently banned nicks'
}
def __init__(self, channels, nickname, server, **kwargs):
self.port = kwargs.get('port', 6667)
self.ops = kwargs.get('ops', [])
@ -36,21 +50,6 @@ class Bot(irc.bot.SingleServerIRCBot):
self.chanlist = channels
self.bot_nick = nickname
self.start_logging()
self.output_message = plugin.message
self.output_action = plugin.action
self.internal_commands = {
'join': 'join a channel',
'quit': 'force the bot to quit',
'reload': 'force bot to reload all plugins',
'enable': 'enable a plugin',
'disable': 'disable a plugin',
'op': 'add a user as bot operator',
'deop': 'remove a user as bot operator',
'ops': 'list all ops',
'ban': 'ban a user from using the bot',
'unban': 'remove bot ban for user',
'banlist': 'currently banned nicks'
}
self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)
@ -72,9 +71,9 @@ class Bot(irc.bot.SingleServerIRCBot):
if cmd:
self.cmd = cmd
self.arg = arg
if text:
if text != None:
self.text = text
if not (cmd or text):
if not (cmd==None or text==None):
raise TypeError('missing cmd or text parameter')
def start_logging(self):
@ -140,7 +139,7 @@ class Bot(irc.bot.SingleServerIRCBot):
try:
c.join(*arg.split())
self.logger.info('joining {} per request of {}'.format(arg, nick))
output = self.output_message('{}: joined {}'.format(nick, arg.split()[0]))
output = plugin.message('{}: joined {}'.format(nick, arg.split()[0]))
except:
self.logger.exception('issue with join command: {}join #channel <channel key>'.format(self.cmd_prefix))
elif cmd == 'quit' and op:
@ -154,43 +153,43 @@ class Bot(irc.bot.SingleServerIRCBot):
elif cmd == 'reload' and op:
self.logger.info('reloading plugins per request of {}'.format(nick))
plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)
output = self.output_message('Plugins reloaded')
output = plugin.message('Plugins reloaded')
elif cmd == 'enable' and op:
if arg in plugin.plugins:
if plugin.plugins[arg].enabled:
output = self.output_message("{}: '{}' already enabled".format(nick, arg))
output = plugin.message("{}: '{}' already enabled".format(nick, arg))
else:
plugin.plugins[arg].enable()
output = self.output_message("{}: '{}' enabled!".format(nick, arg))
output = plugin.message("{}: '{}' enabled!".format(nick, arg))
else:
output = self.output_message("{}: '{}' not found".format(nick, arg))
output = plugin.message("{}: '{}' not found".format(nick, arg))
elif cmd == 'disable' and op:
if arg in plugin.plugins:
if not plugin.plugins[arg].enabled:
output = self.output_message("{}: '{}' already disabled".format(nick, arg))
output = plugin.message("{}: '{}' already disabled".format(nick, arg))
else:
plugin.plugins[arg].disable()
output = self.output_message("{}: '{}' disabled!".format(nick, arg))
output = plugin.message("{}: '{}' disabled!".format(nick, arg))
elif cmd == 'op' and op:
for o in arg.split(' '):
self.ops.append(o)
output = self.output_message('{}: {} added as op'.format(nick, arg))
output = plugin.message('{}: {} added as op'.format(nick, arg))
elif cmd == 'deop' and op:
for o in arg.split(' '):
self.ops = [i for i in self.ops if i != o]
output = self.output_message('{}: {} removed as op'.format(nick, arg))
output = plugin.message('{}: {} removed as op'.format(nick, arg))
elif cmd == 'ops' and op:
output = self.output_message('current ops: {}'.format(', '.join(self.ops)))
output = plugin.message('current ops: {}'.format(', '.join(self.ops)))
elif cmd == 'ban' and op:
for o in arg.split(' '):
self.banned_users.append(o)
output = self.output_message('{}: banned {}'.format(nick, arg))
output = plugin.message('{}: banned {}'.format(nick, arg))
elif cmd == 'unban' and op:
for o in arg.split(' '):
self.banned_users = [i for i in self.banned_users if i != o]
output = self.output_message('{}: removed ban for {}'.format(nick, arg))
output = plugin.message('{}: removed ban for {}'.format(nick, arg))
elif cmd == 'banlist':
output = self.output_message('currently banned: {}'.format(', '.join(self.banned_users)))
output = plugin.message('currently banned: {}'.format(', '.join(self.banned_users)))
return output
def call_plugins(self, privmsg, action, notice, chan, cmd, text, nick_list, nick, arg, msg_type):
@ -199,7 +198,7 @@ class Bot(irc.bot.SingleServerIRCBot):
try:
if plugin.cmds[cmd].ops and nick not in self.ops:
if plugin.cmds[cmd].ops_msg:
output = self.output_message(plugin.cmds[cmd].ops_msg)
output = plugin.message(plugin.cmds[cmd].ops_msg)
elif plugin.cmds[cmd].enabled:
self.logger.debug('executing {}'.format(cmd))
output = plugin.cmds[cmd].run(self.Message(
@ -248,7 +247,12 @@ class Bot(irc.bot.SingleServerIRCBot):
def process_event(self, c, e):
nick = e.source.nick
text = e.arguments[0]
if nick == self.bot_nick:
pass
if e.arguments:
text = e.arguments[0]
else:
text = ''
if e.type == 'privmsg' or e.type == 'pubmsg':
msg_type = 'message'
else:
@ -313,18 +317,24 @@ class Bot(irc.bot.SingleServerIRCBot):
class TwitchBot(Bot):
def __init__(self, nickname, channel, token, plugin_dir='plugins', log_level='info', ops=[]):
def __init__(self, nickname, channel, token, **kwargs):
self.port = kwargs.get('port', 6667)
self.ops = kwargs.get('ops', [])
self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
self.log_level = kwargs.get('log_level', 'info')
self.log_file = kwargs.get('log_file', None)
self.server_pass = kwargs.get('server_pass', None)
self.cmd_prefix = kwargs.get('cmd_prefix', '!')
self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
self.disable_help = kwargs.get('disable_help', False)
self.banned_users = kwargs.get('banned_users', [])
self.bot_nick = nickname
self.log_level = log_level
self.start_logging()
self.channel = channel
self.plugin_dir = plugin_dir
self.ops = ops
server = 'irc.twitch.tv'
port = 6667
self.logger.info('Joining Twitch Server')
irc.bot.SingleServerIRCBot.__init__(self, [(server, port, 'oauth:'+token)], nickname, nickname)
plugin.load_plugins(self.plugin_dir)
irc.bot.SingleServerIRCBot.__init__(self, [('irc.twitch.tv', 6667, 'oauth:'+token)], nickname, nickname)
self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)
def on_welcome(self, c, e):
self.logger.info('requesting permissions')
@ -333,4 +343,3 @@ class TwitchBot(Bot):
c.cap('REQ', ':twitch.tv/commands')
self.logger.info('Joining channel ' + self.channel)
c.join(self.channel)

View File

@ -27,7 +27,7 @@ def read_conf(config, conf_format):
elif config.name.endswith(('.toml', '.tml')):
conf_format = 'toml'
else:
click.echo('Could not detect file format, please supply using --format option', err=True)
raise click.ClickException('Could not detect file format, please supply using --format option')
if conf_format == 'json':
import json
to_json = json.loads(config.read())
@ -36,7 +36,7 @@ def read_conf(config, conf_format):
try:
import yaml
except ImportError:
click.echo('yaml not installed, please use `pip3 install pinhook[yaml]` to install', err=True)
raise click.ClickException('yaml not installed, please use `pip3 install pinhook[yaml]` to install')
else:
to_yaml = yaml.load(config.read(), Loader=yaml.FullLoader)
output = schema.load(to_yaml)
@ -44,7 +44,7 @@ def read_conf(config, conf_format):
try:
import toml
except ImportError:
click.echo('toml not installed, please use `pip3 install pinhook[toml]` to install', err=True)
raise click.ClicKException('toml not installed, please use `pip3 install pinhook[toml]` to install')
else:
to_toml = toml.load(config.name)
output = schema.load(to_toml)