mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-12-11 20:59:42 +01:00
Compare commits
13 Commits
1952dae6f7
...
8fe517f48a
Author | SHA1 | Date | |
---|---|---|---|
|
8fe517f48a | ||
|
45c7615f4a | ||
|
d308329461 | ||
|
aa6bd7257d | ||
|
bdb80b196a | ||
|
0f1011081e | ||
|
e19282a2d3 | ||
|
5baf87ddba | ||
|
0af4af16d3 | ||
|
64ae28c0b8 | ||
|
b8aa5aa33e | ||
|
c23227cdc7 | ||
|
6b72672a1e |
62
.github/workflows/test.yml
vendored
Normal file
62
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
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
54
.travis.yml
@ -1,54 +0,0 @@
|
||||
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
|
@ -13,7 +13,7 @@ Searches for results on DuckDuckGo.
|
||||
|
||||
Example::
|
||||
|
||||
<+GLolol> %ddg search eiffel tower
|
||||
<+jlu5> %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:
|
||||
|
@ -55,7 +55,7 @@ class DDG(callbacks.Plugin):
|
||||
|
||||
Example::
|
||||
|
||||
<+GLolol> %ddg search eiffel tower
|
||||
<+jlu5> %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>
|
||||
|
||||
"""
|
||||
|
@ -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/GLolol/SupyPlugins/tree/master/DDG>.')
|
||||
'<https://github.com/jlu5/SupyPlugins/tree/master/DDG>.')
|
||||
ref = self.registryValue('referer')
|
||||
if not ref:
|
||||
ref = 'http://%s/%s' % (dynamic.irc.server,
|
||||
|
@ -129,8 +129,13 @@ class MessageParser(callbacks.Plugin, plugins.ChannelDBHandler):
|
||||
|
||||
def _runCommandFunction(self, irc, msg, command):
|
||||
"""Run a command from message, as if command was sent over IRC."""
|
||||
tokens = callbacks.tokenize(command,
|
||||
channel=msg.channel, network=irc.network)
|
||||
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:
|
||||
|
@ -85,6 +85,11 @@ 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')
|
||||
|
@ -37,7 +37,7 @@ Examples
|
||||
< Mikaela> @load PluginDownloader
|
||||
< Limnoria> Ok.
|
||||
< Mikaela> @plugindownloader repolist
|
||||
< 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
|
||||
< 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
|
||||
< 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
|
||||
|
@ -348,7 +348,7 @@ class PluginDownloader(callbacks.Plugin):
|
||||
< Mikaela> @load PluginDownloader
|
||||
< Limnoria> Ok.
|
||||
< Mikaela> @plugindownloader repolist
|
||||
< 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
|
||||
< 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
|
||||
< 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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -102,6 +102,7 @@ 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')
|
||||
|
@ -16,9 +16,9 @@ After enabling SedRegex, typing a regex in the form
|
||||
|
||||
::
|
||||
|
||||
20:24 <~GL> helli world
|
||||
20:24 <~GL> s/i/o/
|
||||
20:24 <@Lily> GL meant to say: hello world
|
||||
20:24 <jlu5> helli world
|
||||
20:24 <jlu5> s/i/o/
|
||||
20:24 <Limnoria> jlu5 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,
|
||||
|
@ -69,9 +69,9 @@ class SedRegex(callbacks.PluginRegexp):
|
||||
|
||||
::
|
||||
|
||||
20:24 <~GL> helli world
|
||||
20:24 <~GL> s/i/o/
|
||||
20:24 <@Lily> GL meant to say: hello world
|
||||
20:24 <jlu5> helli world
|
||||
20:24 <jlu5> s/i/o/
|
||||
20:24 <Limnoria> jlu5 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,
|
||||
|
@ -146,6 +146,9 @@ 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'])
|
||||
|
11
setup.py
11
setup.py
@ -82,10 +82,19 @@ if version:
|
||||
fd.close()
|
||||
|
||||
if sys.version_info < (3, 4, 0):
|
||||
sys.stderr.write("Limnoria requires Python 3.4 or newer.")
|
||||
sys.stderr.write("Limnoria requires Python 3.6 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
|
||||
|
||||
|
@ -114,6 +114,13 @@ 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)
|
||||
@ -208,6 +215,8 @@ 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
|
||||
|
@ -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):
|
||||
def addUser(self, user, prefix_chars='@%+&~!'):
|
||||
"Adds a given user to the ChannelState. Power prefixes are handled."
|
||||
nick = user.lstrip('@%+&~!')
|
||||
nick = user.lstrip(prefix_chars)
|
||||
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 '@%+&~!':
|
||||
while user and user[0] in prefix_chars:
|
||||
(marker, user) = (user[0], user[1:])
|
||||
assert user, 'Looks like my caller is passing chars, not nicks.'
|
||||
if marker in '@&~!':
|
||||
@ -963,13 +963,27 @@ 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():
|
||||
if ircutils.isUserHostmask(item):
|
||||
name = ircutils.nickFromHostmask(item)
|
||||
self.nicksToHostmasks[name] = name
|
||||
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
|
||||
else:
|
||||
name = item
|
||||
c.addUser(name)
|
||||
c.addUser(name, prefix_chars=prefix_chars)
|
||||
|
||||
if type == '@':
|
||||
c.modes['s'] = None
|
||||
|
||||
|
@ -60,7 +60,11 @@ def debug(s, *args):
|
||||
"""Prints a debug string. Most likely replaced by our logging debug."""
|
||||
print('***', s % args)
|
||||
|
||||
userHostmaskRe = re.compile(r'^\S+!\S+@\S+$')
|
||||
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+?)$')
|
||||
def isUserHostmask(s):
|
||||
"""Returns whether or not the string s is a valid User hostmask."""
|
||||
return userHostmaskRe.match(s) is not None
|
||||
@ -91,9 +95,17 @@ def hostFromHostmask(hostmask):
|
||||
def splitHostmask(hostmask):
|
||||
"""hostmask => (nick, user, host)
|
||||
Returns the nick, user, host of a user hostmask."""
|
||||
assert isUserHostmask(hostmask)
|
||||
nick, rest = hostmask.rsplit('!', 1)
|
||||
user, host = rest.rsplit('@', 1)
|
||||
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
|
||||
return (minisix.intern(nick), minisix.intern(user), minisix.intern(host))
|
||||
|
||||
def joinHostmask(nick, ident, host):
|
||||
|
@ -329,6 +329,7 @@ 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():
|
||||
|
@ -529,6 +529,42 @@ 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):
|
||||
|
@ -113,6 +113,24 @@ 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('&'))
|
||||
|
Loading…
Reference in New Issue
Block a user