Compare commits

..

No commits in common. "8fe517f48aadfa55148142a1cfd1e14df907feac" and "1952dae6f7b9ed14231d86bda5ca23f874f231ae" have entirely different histories.

22 changed files with 81 additions and 202 deletions

View File

@ -1,62 +0,0 @@
name: Test
on:
push:
pull_request:
jobs:
build:
runs-on: ${{ matrix.runs-on }}
strategy:
matrix:
python-version: ["3.5", "3.6", "3.7", "3.8", "3.9", "3.10.0-beta.4", "pypy-3.6", "pypy-3.7"]
with-opt-deps: [false, true]
runs-on: [ubuntu-latest]
exclude:
# Some of the dependencies don't work on old Python versions
- python-version: "3.4"
with-opt-deps: true
- python-version: "3.5"
with-opt-deps: true
- python-version: "3.6"
with-opt-deps: true
- python-version: "pypy-3.6"
with-opt-deps: true
include:
- python-version: "3.4"
with-opt-deps: false
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Upgrade pip
run: |
python3 -m pip install --upgrade pip
- name: Install optional dependencies
if: ${{ matrix.with-opt-deps }}
run: |
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt
- name: Install
run: |
LIMNORIA_WARN_OLD_PYTHON=0 python3 setup.py install
- name: Test with unittest
run: |
supybot-test test -v --plugins-dir=./plugins/ --no-network
- name: Test with irctest
if: "${{ matrix.with-opt-deps && matrix.python-version != 'pypy-3.7' }}"
run: |
git clone https://github.com/ProgVal/irctest.git
cd irctest
pip3 install -r requirements.txt
make limnoria

54
.travis.yml Normal file
View File

@ -0,0 +1,54 @@
language: python
sudo: true
install:
- if [ "$WITH_OPT_DEPS" = "true" ] ; then pip install -vr requirements.txt pytest; fi
- git clone https://github.com/ProgVal/irctest.git
- echo "y" | pip uninstall limnoria || true
# command to run tests, e.g. python setup.py test
script:
- echo $TRAVIS_PYTHON_VERSION
- python setup.py install
- supybot-test test -v --plugins-dir=./plugins/ --no-network
- if [ "$WITH_OPT_DEPS" = "true" ] -a [[ "$TRAVIS_PYTHON_VERSION" =~ ^3\.[4-9] ]] ; then cd irctest; pytest --controllers irctest.controllers.limnoria; fi
notifications:
email: false
matrix:
include:
- python: "3.4"
env: WITH_OPT_DEPS=false
dist: trusty
- python: "3.5"
env: WITH_OPT_DEPS=false
dist: trusty
- python: "3.6"
env: WITH_OPT_DEPS=false
dist: trusty
- python: "3.7"
env: WITH_OPT_DEPS=false
dist: xenial
- python: "3.7"
env: WITH_OPT_DEPS=true
dist: xenial
- python: "3.8"
env: WITH_OPT_DEPS=true
dist: xenial
- python: "3.9"
env: WITH_OPT_DEPS=true
dist: xenial
- python: "nightly"
env: WITH_OPT_DEPS=true
dist: xenial
- python: "pypy3"
env: WITH_OPT_DEPS=false
dist: trusty
- python: "nightly"
env: WITH_OPT_DEPS=true
dist: xenial
allow_failures:
- python: "pypy3"
env: WITH_OPT_DEPS=true
dist: xenial

View File

@ -13,7 +13,7 @@ Searches for results on DuckDuckGo.
Example::
<+jlu5> %ddg search eiffel tower
<+GLolol> %ddg search eiffel tower
<@Atlas> The Eiffel Tower is an iron lattice tower located on the Champ de Mars in Paris. It was named after the engineer Gustave Eiffel, whose company designed and built the tower. - <https://en.wikipedia.org/wiki/Eiffel_Tower>
.. _commands-DDG:

View File

@ -55,7 +55,7 @@ class DDG(callbacks.Plugin):
Example::
<+jlu5> %ddg search eiffel tower
<+GLolol> %ddg search eiffel tower
<@Atlas> The Eiffel Tower is an iron lattice tower located on the Champ de Mars in Paris. It was named after the engineer Gustave Eiffel, whose company designed and built the tower. - <https://en.wikipedia.org/wiki/Eiffel_Tower>
"""

View File

@ -126,7 +126,7 @@ class Google(callbacks.PluginRegexp):
'plugin for your searches, like '
'<https://github.com/Hoaas/Supybot-plugins/tree/master/DuckDuckGo>, '
'<https://github.com/joulez/GoogleCSE>, or '
'<https://github.com/jlu5/SupyPlugins/tree/master/DDG>.')
'<https://github.com/GLolol/SupyPlugins/tree/master/DDG>.')
ref = self.registryValue('referer')
if not ref:
ref = 'http://%s/%s' % (dynamic.irc.server,

View File

@ -129,13 +129,8 @@ class MessageParser(callbacks.Plugin, plugins.ChannelDBHandler):
def _runCommandFunction(self, irc, msg, command):
"""Run a command from message, as if command was sent over IRC."""
try:
tokens = callbacks.tokenize(command,
channel=msg.channel, network=irc.network)
except SyntaxError as e:
# Emulate what callbacks.py does
self.log.debug('Error return: %s', utils.exnToString(e))
irc.error(str(e))
try:
self.Proxy(irc.irc, msg, tokens)
except Exception as e:

View File

@ -85,11 +85,6 @@ class MessageParserTestCase(ChannelPluginTestCase):
self.assertResponse(' ', '$1')
self.assertNotError('messageparser remove "this( .+)? a(.*)"')
def testSyntaxError(self):
self.assertNotError(r'messageparser add "test" "echo foo \" bar"')
self.feedMsg('test')
self.assertResponse(' ', 'Error: No closing quotation')
def testShow(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser show "nostuff"', 'there is no such regexp trigger')

View File

@ -37,7 +37,7 @@ Examples
< Mikaela> @load PluginDownloader
< Limnoria> Ok.
< Mikaela> @plugindownloader repolist
< Limnoria> Antibody, jlu5, Hoaas, Iota, ProgVal, SpiderDave, boombot, code4lib, code4lib-edsu, code4lib-snapshot, doorbot, frumious, jonimoose, mailed-notifier, mtughan-weather, nanotube-bitcoin, nyuszika7h, nyuszika7h-old, pingdom, quantumlemur, resistivecorpse, scrum, skgsergio, stepnem
< Limnoria> Antibody, GLolol, Hoaas, Iota, ProgVal, SpiderDave, boombot, code4lib, code4lib-edsu, code4lib-snapshot, doorbot, frumious, jonimoose, mailed-notifier, mtughan-weather, nanotube-bitcoin, nyuszika7h, nyuszika7h-old, pingdom, quantumlemur, resistivecorpse, scrum, skgsergio, stepnem
< Mikaela> @plugindownloader repolist ProgVal
< Limnoria> AttackProtector, AutoTrans, Biography, Brainfuck, ChannelStatus, Cleverbot, Coffee, Coinpan, Debian, ERepublik, Eureka, Fortune, GUI, GitHub, Glob2Chan, GoodFrench, I18nPlaceholder, IMDb, IgnoreNonVoice, Iwant, Kickme, LimnoriaChan, LinkRelay, ListEmpty, Listener, Markovgen, MegaHAL, MilleBornes, NoLatin1, NoisyKarma, OEIS, PPP, PingTime, Pinglist, RateLimit, Rbls, Redmine, Scheme, Seeks, (1 more message)
< Mikaela> more

View File

@ -348,7 +348,7 @@ class PluginDownloader(callbacks.Plugin):
< Mikaela> @load PluginDownloader
< Limnoria> Ok.
< Mikaela> @plugindownloader repolist
< Limnoria> Antibody, jlu5, Hoaas, Iota, ProgVal, SpiderDave, boombot, code4lib, code4lib-edsu, code4lib-snapshot, doorbot, frumious, jonimoose, mailed-notifier, mtughan-weather, nanotube-bitcoin, nyuszika7h, nyuszika7h-old, pingdom, quantumlemur, resistivecorpse, scrum, skgsergio, stepnem
< Limnoria> Antibody, GLolol, Hoaas, Iota, ProgVal, SpiderDave, boombot, code4lib, code4lib-edsu, code4lib-snapshot, doorbot, frumious, jonimoose, mailed-notifier, mtughan-weather, nanotube-bitcoin, nyuszika7h, nyuszika7h-old, pingdom, quantumlemur, resistivecorpse, scrum, skgsergio, stepnem
< Mikaela> @plugindownloader repolist ProgVal
< Limnoria> AttackProtector, AutoTrans, Biography, Brainfuck, ChannelStatus, Cleverbot, Coffee, Coinpan, Debian, ERepublik, Eureka, Fortune, GUI, GitHub, Glob2Chan, GoodFrench, I18nPlaceholder, IMDb, IgnoreNonVoice, Iwant, Kickme, LimnoriaChan, LinkRelay, ListEmpty, Listener, Markovgen, MegaHAL, MilleBornes, NoLatin1, NoisyKarma, OEIS, PPP, PingTime, Pinglist, RateLimit, Rbls, Redmine, Scheme, Seeks, (1 more message)
< Mikaela> more

View File

@ -25,7 +25,7 @@ Creates a poll that can be voted on in this way::
And results::
<admin> @poll results
<bot> 2 votes for No, 1 vote for Yes, and 0 votes for Maybe
<bot> 2 votes for No, 1 vote for Yes, and 0 votes for Maybe",
Longer answers are possible, and voters only need to use the first
word of each answer to vote. For example, this creates a poll that

View File

@ -64,7 +64,7 @@ class Poll_(callbacks.Plugin):
And results::
<admin> @poll results
<bot> 2 votes for No, 1 vote for Yes, and 0 votes for Maybe
<bot> 2 votes for No, 1 vote for Yes, and 0 votes for Maybe",
Longer answers are possible, and voters only need to use the first
word of each answer to vote. For example, this creates a poll that

View File

@ -102,7 +102,6 @@ class RSSTestCase(ChannelPluginTestCase):
@mock_urllib
def testRemoveAliasedFeed(self, mock):
mock._data = xkcd_new
try:
self.assertNotError('rss announce add http://xkcd.com/rss.xml')
self.assertNotError('rss add xkcd http://xkcd.com/rss.xml')

View File

@ -16,9 +16,9 @@ After enabling SedRegex, typing a regex in the form
::
20:24 <jlu5> helli world
20:24 <jlu5> s/i/o/
20:24 <Limnoria> jlu5 meant to say: hello world
20:24 <~GL> helli world
20:24 <~GL> s/i/o/
20:24 <@Lily> GL meant to say: hello world
You can also do ``othernick: s/text/replacement/`` to only replace
messages from a certain user. Supybot ignores are respected by the plugin,

View File

@ -69,9 +69,9 @@ class SedRegex(callbacks.PluginRegexp):
::
20:24 <jlu5> helli world
20:24 <jlu5> s/i/o/
20:24 <Limnoria> jlu5 meant to say: hello world
20:24 <~GL> helli world
20:24 <~GL> s/i/o/
20:24 <@Lily> GL meant to say: hello world
You can also do ``othernick: s/text/replacement/`` to only replace
messages from a certain user. Supybot ignores are respected by the plugin,

View File

@ -146,9 +146,6 @@ class User(callbacks.Plugin):
user.setPassword(password)
if addHostmask:
user.addHostmask(msg.prefix)
account = msg.server_tags.get('account')
if account:
user.addNick(irc.network, account)
ircdb.users.setUser(user)
irc.replySuccess()
register = wrap(register, ['private', 'something', 'something'])

View File

@ -82,19 +82,10 @@ if version:
fd.close()
if sys.version_info < (3, 4, 0):
sys.stderr.write("Limnoria requires Python 3.6 or newer.")
sys.stderr.write("Limnoria requires Python 3.4 or newer.")
sys.stderr.write(os.linesep)
sys.exit(-1)
if sys.version_info < (3, 6, 0) \
and os.environ.get('LIMNORIA_WARN_OLD_PYTHON') != '0':
sys.stderr.write('====================================================\n')
sys.stderr.write('Limnoria support for Python versions older than 3.6\n')
sys.stderr.write('is deprecated and may be removed in the near future.\n')
sys.stderr.write('You should upgrade ASAP.\n')
sys.stderr.write('Install will continue in 60s.\n')
sys.stderr.write('====================================================\n')
time.sleep(60)
import textwrap

View File

@ -114,13 +114,6 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
except:
pass
self.connected = False
if self.irc is None:
# This driver is dead already, but we're still running because
# of select() running in an other driver's thread that started
# before this one died and stil holding a reference to this
# instance.
# Just return, and we should never be called again.
return
self.scheduleReconnect()
else:
log.debug('Got EAGAIN, current count: %s.', self.eagains)
@ -215,8 +208,6 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
msg = drivers.parseMsg(line)
if msg is not None and self.irc is not None:
# self.irc may be None if this driver is already dead,
# see comment in _handleSocketError
self.irc.feedMsg(msg)
except socket.timeout:
pass

View File

@ -389,15 +389,15 @@ class ChannelState(utils.python.Object):
"""Returns whether the given nick is an halfop, or an op."""
return nick in self.halfops or nick in self.ops
def addUser(self, user, prefix_chars='@%+&~!'):
def addUser(self, user):
"Adds a given user to the ChannelState. Power prefixes are handled."
nick = user.lstrip(prefix_chars)
nick = user.lstrip('@%+&~!')
if not nick:
return
# & is used to denote protected users in UnrealIRCd
# ~ is used to denote channel owner in UnrealIRCd
# ! is used to denote protected users in UltimateIRCd
while user and user[0] in prefix_chars:
while user and user[0] in '@%+&~!':
(marker, user) = (user[0], user[1:])
assert user, 'Looks like my caller is passing chars, not nicks.'
if marker in '@&~!':
@ -963,27 +963,13 @@ class IrcState(IrcCommandDispatcher, log.Firewalled):
if channel not in self.channels:
self.channels[channel] = ChannelState()
c = self.channels[channel]
# Set of prefixes servers may append before a NAMES reply when
# the user is op/halfop/voice/...
# https://datatracker.ietf.org/doc/html/draft-hardy-irc-isupport-00#section-4.15
prefix = self.supported.get('PREFIX')
if prefix is None:
prefix_chars = '@%+&~!' # see the comments in addUser
else:
prefix_chars = ''.join(prefix.values())
for item in items.split():
stripped_item = item.lstrip(prefix_chars)
item_prefix = item[0:-len(stripped_item)]
if ircutils.isUserHostmask(stripped_item):
nick = ircutils.nickFromHostmask(stripped_item)
self.nicksToHostmasks[nick] = stripped_item
name = item_prefix + nick
if ircutils.isUserHostmask(item):
name = ircutils.nickFromHostmask(item)
self.nicksToHostmasks[name] = name
else:
name = item
c.addUser(name, prefix_chars=prefix_chars)
c.addUser(name)
if type == '@':
c.modes['s'] = None

View File

@ -60,11 +60,7 @@ def debug(s, *args):
"""Prints a debug string. Most likely replaced by our logging debug."""
print('***', s % args)
def warning(s, *args):
"""Prints a debug string. Most likely replaced by our logging debug."""
print('###', s % args)
userHostmaskRe = re.compile(r'^(?P<nick>\S+?)!(?P<user>\S+)@(?P<host>\S+?)$')
userHostmaskRe = re.compile(r'^\S+!\S+@\S+$')
def isUserHostmask(s):
"""Returns whether or not the string s is a valid User hostmask."""
return userHostmaskRe.match(s) is not None
@ -95,17 +91,9 @@ def hostFromHostmask(hostmask):
def splitHostmask(hostmask):
"""hostmask => (nick, user, host)
Returns the nick, user, host of a user hostmask."""
m = userHostmaskRe.match(hostmask)
assert m is not None, hostmask
nick = m.group("nick")
user = m.group("user")
host = m.group("host")
if set("!@") & set(nick+user+host):
# There should never be either of these characters in the part.
# As far as I know it never happens in practice aside from networks
# broken by design.
warning("Invalid hostmask format: %s", hostmask)
# TODO: error if strictRfc is True
assert isUserHostmask(hostmask)
nick, rest = hostmask.rsplit('!', 1)
user, host = rest.rsplit('@', 1)
return (minisix.intern(nick), minisix.intern(user), minisix.intern(host))
def joinHostmask(nick, ident, host):

View File

@ -329,7 +329,6 @@ atexit.register(logging.shutdown)
# ircutils will work without this, but it's useful.
ircutils.debug = debug
ircutils.warning = warning
def getPluginLogger(name):
if not conf.supybot.log.plugins.individualLogfiles():

View File

@ -529,42 +529,6 @@ class IrcStateTestCase(SupyTestCase):
st = irclib.IrcState()
self.assert_(st.addMsg(self.irc, ircmsgs.IrcMsg('MODE foo +i')) or 1)
def testNamreply(self):
"""RPL_NAMREPLY / reply to NAMES"""
# Just nicks (à la RFC 1459 + some common prefix chars)
st = irclib.IrcState()
st.addMsg(self.irc, ircmsgs.IrcMsg(command='353',
args=('*', '=', '#chan', 'nick1 @nick2 ~@nick3')))
chan_st = st.channels['#chan']
self.assertEqual(chan_st.users, ircutils.IrcSet(['nick1', 'nick2', 'nick3']))
self.assertEqual(chan_st.ops, ircutils.IrcSet(['nick2', 'nick3']))
self.assertEqual(st.nicksToHostmasks, ircutils.IrcDict({}))
# with userhost-in-names
st = irclib.IrcState()
st.addMsg(self.irc, ircmsgs.IrcMsg(command='353',
args=('*', '=', '#chan', 'nick1!u1@h1 @nick2!u2@h2 ~@nick3!u3@h3')))
chan_st = st.channels['#chan']
self.assertEqual(chan_st.users, ircutils.IrcSet(['nick1', 'nick2', 'nick3']))
self.assertEqual(chan_st.ops, ircutils.IrcSet(['nick2', 'nick3']))
self.assertEqual(st.nicksToHostmasks['nick1'], 'nick1!u1@h1')
self.assertEqual(st.nicksToHostmasks['nick2'], 'nick2!u2@h2')
self.assertEqual(st.nicksToHostmasks['nick3'], 'nick3!u3@h3')
# Prefixed with chars not in ISUPPORT PREFIX
st = irclib.IrcState()
st.addMsg(self.irc, ircmsgs.IrcMsg(command='005',
args=('*', 'PREFIX=(ov)@+', 'are supported')))
st.addMsg(self.irc, ircmsgs.IrcMsg(command='353',
args=('*', '=', '#chan', 'nick1!u1@h1 @nick2!u2@h2 ~@nick3!u3@h3')))
chan_st = st.channels['#chan']
self.assertEqual(chan_st.users, ircutils.IrcSet(['nick1', 'nick2', '~@nick3']))
self.assertEqual(chan_st.ops, ircutils.IrcSet(['nick2']))
self.assertEqual(st.nicksToHostmasks['nick1'], 'nick1!u1@h1')
self.assertEqual(st.nicksToHostmasks['nick2'], 'nick2!u2@h2')
self.assertEqual(st.nicksToHostmasks['~@nick3'], '~@nick3!u3@h3')
class IrcCapsTestCase(SupyTestCase, CapNegMixin):
def testReqLineLength(self):

View File

@ -113,24 +113,6 @@ class FunctionsTestCase(SupyTestCase):
self.assertFalse(ircutils.isUserHostmask('@'))
self.assertFalse(ircutils.isUserHostmask('!bar@baz'))
def testSplitHostmask(self):
# This is the only valid case:
self.assertEqual(ircutils.splitHostmask('foo!bar@baz'),
('foo', 'bar', 'baz'))
# This ones are technically allowed by RFC1459, but never happens in
# practice:
self.assertEqual(ircutils.splitHostmask('foo!bar!qux@quux'),
('foo', 'bar!qux', 'quux'))
self.assertEqual(ircutils.splitHostmask('foo!bar@baz@quux'),
('foo', 'bar@baz', 'quux'))
self.assertEqual(ircutils.splitHostmask('foo!bar@baz!qux@quux'),
('foo', 'bar@baz!qux', 'quux'))
# And this one in garbage, let's just make sure we don't crash:
self.assertEqual(ircutils.splitHostmask('foo!bar@baz!qux'),
('foo', 'bar', 'baz!qux'))
def testIsChannel(self):
self.assertTrue(ircutils.isChannel('#'))
self.assertTrue(ircutils.isChannel('&'))