Merge branch 'release/1.1'
This commit is contained in:
commit
fa99d1eef5
140 changed files with 1072 additions and 10173 deletions
13
.gitignore
vendored
Normal file → Executable file
13
.gitignore
vendored
Normal file → Executable file
|
@ -1,12 +1,7 @@
|
||||||
|
persist
|
||||||
|
config
|
||||||
|
*.db
|
||||||
|
*.log
|
||||||
.*.swp
|
.*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
*.orig
|
*.orig
|
||||||
persist
|
|
||||||
config
|
|
||||||
pep8.py
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
*.db
|
|
||||||
web
|
|
||||||
control.sh
|
|
||||||
bot.log
|
|
1
DOCUMENTATION
Executable file
1
DOCUMENTATION
Executable file
|
@ -0,0 +1 @@
|
||||||
|
Please see the wiki @ http://git.io/cloudbotwiki
|
0
LICENSE
Normal file → Executable file
0
LICENSE
Normal file → Executable file
127
README.md
Normal file → Executable file
127
README.md
Normal file → Executable file
|
@ -1,40 +1,110 @@
|
||||||
##CloudBot/1.0
|
# CloudBot/1.1
|
||||||
|
|
||||||
### About
|
## About
|
||||||
CloudBot is very heavily based on Skybot by rmmh. (https://github.com/rmmh/skybot)
|
|
||||||
|
|
||||||
### Install
|
CloudBot is a Python IRC bot very heavily based on [Skybot](http://git.io/skybot) by [rmmh](http://git.io/rmmh).
|
||||||
Before you can run the bot, you need to install a few Python modules. These are *LXML* and *BeautifulSoup*. On Ubuntu these can be installed using the following commands:
|
|
||||||
|
|
||||||
```sudo apt-get install python-lxml
|
### Goals
|
||||||
```
|
|
||||||
|
|
||||||
```sudo apt-get install python-beautifulsoup
|
* Easy to use wrapper
|
||||||
```
|
* Intuitive configuration
|
||||||
|
* Fully controlled from IRC
|
||||||
|
* Fully compatable with existing skybot plugins
|
||||||
|
* Easily extendable
|
||||||
|
* Thorough documentation
|
||||||
|
* Cross-platform
|
||||||
|
* Muti-threaded, efficient
|
||||||
|
* Automatic reloading
|
||||||
|
* Little boilerplate
|
||||||
|
|
||||||
For the spell.py plugin to work you need *Enchant*. This can be installed with:
|
## Download
|
||||||
```sudo apt-get install python-enchant
|
|
||||||
```
|
|
||||||
|
|
||||||
The whois.py plugin will work by default, but *not very well*. To make it work properly you will need to install the native *whois* package. This can be installed with:
|
Get CloudBot at [git.io/getcloudbot](http://git.io/getcloudbot "Get CloudBot from Github!").
|
||||||
```sudo apt-get install whois
|
|
||||||
```
|
|
||||||
|
|
||||||
If you use another OS or distro you can find source packages on the module(s) web site, or you can try to find suitable packages in your package manager.
|
Unzip the resulting file, and continue to read this document.
|
||||||
|
|
||||||
Once installing these packages run the bot once with ```python bot.py``` to generate the config file. Stop the bot, edit the config, and run the bot again with ```python bot.py``` to start it up :)
|
## Install
|
||||||
|
|
||||||
### Requirements
|
Before you can run the bot, you need to install a few Python modules. These are `lXML`, `BeautifulSoup`, `MyGengo`, and `HTTPlib2`. These can be installed with PIP (The Python package manager):
|
||||||
CloudBot runs on Python 2.7. Many of the plugins require lxml and BeautifulSoup. It is devloped on Ubuntu 11.10 with Python 2.7.2.
|
|
||||||
|
|
||||||
### License
|
`sudo pip install lxml`
|
||||||
CloudBot is licenced under the GPL v3 license. The terms are as follows.
|
|
||||||
|
|
||||||
|
`sudo pip install beautifulsoup`
|
||||||
|
|
||||||
```
|
`sudo pip install mygengo`
|
||||||
CloudBot 1.0
|
|
||||||
|
|
||||||
Copyright © 2011 Luke Rogers. <http://www.dempltr.com/> <lukeroge@gmail.com>
|
`sudo pip install httplib2`
|
||||||
|
|
||||||
|
On Debian based systems, you can get pip with
|
||||||
|
|
||||||
|
`sudo apt-get install pip`
|
||||||
|
|
||||||
|
For `.spell` to work, we also need a library called `Enchant`. On Debian based systems, install it with:
|
||||||
|
|
||||||
|
`sudo apt-get install python-enchant`
|
||||||
|
|
||||||
|
In addition, for `.whois` to work optimally, you must have `whois` installed. Again, on Debian based systems, install it with:
|
||||||
|
|
||||||
|
`sudo apt-get install whois`
|
||||||
|
|
||||||
|
For the wrapper to work best, install `screen`, or `daemon`:
|
||||||
|
|
||||||
|
`sudo apt-get install screen`
|
||||||
|
|
||||||
|
`sudo apt-get install daemon`
|
||||||
|
|
||||||
|
If you are a user of another Linux disto, use your package manager to install the dependencies, or, for other operating systems, use **Google** to locate source packages you can install.
|
||||||
|
|
||||||
|
Once you have installed the required dependencies, run the bot☩:
|
||||||
|
|
||||||
|
`./cloudbot start`
|
||||||
|
|
||||||
|
It will generate a default config for you. Once you have edited the config, run it again with:
|
||||||
|
|
||||||
|
`./cloudbot start`
|
||||||
|
|
||||||
|
and it will connect to any server(s) you have added to the config. (Config docs at the [wiki](http://git.io/cloudbotconfig))
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
To configure your CloudBot, visit the [Config Wiki Page](http://git.io/cloudbotconfig).
|
||||||
|
|
||||||
|
To write your own plugins, visit the [Plugin Wiki Page](http://git.io/cloudbotplugins).
|
||||||
|
|
||||||
|
More at the [Wiki Main Page](http://git.io/cloudbotwiki).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
The developers reside in [#CloudBot](irc://irc.esper.net/cloudbot "Connect via IRC to #CloudBot on irc.esper.net) on [EsperNet](http://esper.net) and would be glad to help you.
|
||||||
|
|
||||||
|
If you think you have found a bug/have a idea/suggestion, please **open a issue** here on Github.
|
||||||
|
|
||||||
|
## Example CloudBots
|
||||||
|
|
||||||
|
The developers of CloudBot run two CloudBots on [Espernet](http://esper.net).
|
||||||
|
|
||||||
|
They can both be found in [#CloudBot](irc://irc.esper.net/cloudbot "Connect via IRC to #CloudBot on irc.esper.net).
|
||||||
|
|
||||||
|
**mau5bot** is the stable bot, and runs on the latest release version of CloudBot. (mau5bot is running on **Ubuntu Server** *Oneric Ocelot/11.10* with **Python** *2.7.2*)
|
||||||
|
|
||||||
|
**neerbot** is the unstable bot, and runs on the latest development☩☩ version of CloudBot. (neerbot is running on **Debian** *Wheezy/Testing* with **Python** *2.7.2*)
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
CloudBot runs on **Python** *2.7.x*. It is developed on **Debian** *Wheezy/Testing* with **Python** *2.7.2*.
|
||||||
|
|
||||||
|
It **requires Python modules** `lXML`, `BeautifulSoup`, `Enchant`, `MyGengo`, and `HTTPlib2`.
|
||||||
|
|
||||||
|
The programs `screen` or `daemon` are recomended for the wrapper to run optimaly.
|
||||||
|
|
||||||
|
**Windows** users: Windows compatibility with the wrapper and some plugins is **broken** (such as the ping), but we do intend to add it.☩☩☩
|
||||||
|
|
||||||
|
## License
|
||||||
|
CloudBot is **licensed** under the **GPL v3** license. The terms are as follows.
|
||||||
|
|
||||||
|
CloudBot/1.1
|
||||||
|
|
||||||
|
Copyright © 2011 Luke Rogers <http://www.dempltr.com/> - <lukeroge@gmail.com>
|
||||||
|
|
||||||
CloudBot is free software: you can redistribute it and/or modify
|
CloudBot is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -48,4 +118,11 @@ CloudBot is licenced under the GPL v3 license. The terms are as follows.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with CloudBot. If not, see <http://www.gnu.org/licenses/>.
|
along with CloudBot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
```
|
|
||||||
|
## ☩
|
||||||
|
|
||||||
|
☩ if you prefer to run the bot with a custom backend/run it manually, or are on **Windows**, run the bot with `./bot.py`
|
||||||
|
|
||||||
|
☩☩ or whatever version [neersighted](http://git.io/neersighted) is currently hacking on
|
||||||
|
|
||||||
|
☩☩☩ eventually
|
||||||
|
|
2
bot.py
2
bot.py
|
@ -13,7 +13,7 @@ os.chdir(sys.path[0] or '.') # do stuff relative to the install directory
|
||||||
class Bot(object):
|
class Bot(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print 'Welcome to Cloudbot - Version 1.0 - https://github.com/lukeroge/CloudBot/'
|
print 'Welcome to Cloudbot - Version 1.1 - http://git.io/cloudbot'
|
||||||
|
|
||||||
bot = Bot()
|
bot = Bot()
|
||||||
|
|
||||||
|
|
124
cloudbot
Executable file
124
cloudbot
Executable file
|
@ -0,0 +1,124 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo ""
|
||||||
|
echo " ________ ______ __ "
|
||||||
|
echo " / ____/ /___ __ ______/ / __ )____ / /_"
|
||||||
|
echo " / / / / __ \/ / / / __ / __ / __ \/ __/"
|
||||||
|
echo "/ /___/ / /_/ / /_/ / /_/ / /_/ / /_/ / /_ "
|
||||||
|
echo "\____/_/\____/\__,_/\__,_/_____/\____/\__/ "
|
||||||
|
echo " http://git.io/cloudbot by lukeroge "
|
||||||
|
echo ""
|
||||||
|
locatefiles() {
|
||||||
|
botfile="/bot.py"
|
||||||
|
botfile=$(pwd)$botfile
|
||||||
|
logfile="/bot.log"
|
||||||
|
logfile=$(pwd)$logfile
|
||||||
|
}
|
||||||
|
|
||||||
|
running() {
|
||||||
|
if [[ $(ps aux|grep bot.py|grep -v grep|grep -v daemon|grep -v screen) != "" ]]; then
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
checkbackend() {
|
||||||
|
if dpkg -l| grep ^ii|grep daemon|grep 'turns other' > /dev/null; then
|
||||||
|
backend="daemon"
|
||||||
|
elif dpkg -l| grep ^ii|grep screen|grep 'terminal multi' > /dev/null; then
|
||||||
|
backend="screen"
|
||||||
|
else
|
||||||
|
backend="manual"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
setcommands() {
|
||||||
|
status() {
|
||||||
|
if running; then
|
||||||
|
echo "CloudBot is running!"
|
||||||
|
else
|
||||||
|
echo "CloudBot is not running!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
clear() {
|
||||||
|
: > $logfile
|
||||||
|
}
|
||||||
|
if [ "$backend" == "daemon" ]; then
|
||||||
|
start() {
|
||||||
|
daemon -r -n cloudbot -O $logfile python $botfile
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
daemon -n cloudbot --stop
|
||||||
|
}
|
||||||
|
elif [ "$backend" == "screen" ]; then
|
||||||
|
start() {
|
||||||
|
screen -d -m -S cloudbot -t cloudbot python $botfile > $logfile 2>&1
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
proc=`ps ax|grep -v grep|grep screen|grep $botfile`
|
||||||
|
pid=`top -n 1 -p ${proc:0:5} | grep ${proc:0:5}`
|
||||||
|
kill $pid
|
||||||
|
}
|
||||||
|
elif [ "$backend" == "manual" ]; then
|
||||||
|
start() {
|
||||||
|
$botfile
|
||||||
|
}
|
||||||
|
stop() {
|
||||||
|
proc=`ps ax|grep -v grep|grep python|grep $botfile`
|
||||||
|
pid=`top -n 1 -p ${proc:0:5} | grep ${proc:0:5}`
|
||||||
|
kill $pid
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
processargs() {
|
||||||
|
case $1 in
|
||||||
|
start)
|
||||||
|
if running; then
|
||||||
|
echo "Cannot start! Bot is already running!"
|
||||||
|
else
|
||||||
|
echo "Starting CloudBot... ($backend)"
|
||||||
|
start
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
if running; then
|
||||||
|
echo "Stopping CloudBot... ($backend)"
|
||||||
|
stop
|
||||||
|
else
|
||||||
|
echo "Cannot stop! Bot is not already running!"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
if running; then
|
||||||
|
echo "Restarting CloudBot... ($backend)"
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
else
|
||||||
|
echo "Cannot restart! Bot is not already running!"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
clear)
|
||||||
|
echo "Clearing logs..."
|
||||||
|
clear
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Please enter a command:"
|
||||||
|
usage="./cloudbot {start|stop|restart|clear|status}"
|
||||||
|
echo $usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
locatefiles
|
||||||
|
checkbackend
|
||||||
|
setcommands
|
||||||
|
processargs $1
|
||||||
|
}
|
||||||
|
|
||||||
|
main $*
|
48
core/config.py
Normal file → Executable file
48
core/config.py
Normal file → Executable file
|
@ -12,17 +12,19 @@ if not os.path.exists('config'):
|
||||||
{
|
{
|
||||||
"connections":
|
"connections":
|
||||||
{
|
{
|
||||||
"test connection":
|
"EsperNet":
|
||||||
{
|
{
|
||||||
"server": "localhost",
|
"server": "irc.esper.net",
|
||||||
"nick": "nickname",
|
"nick": "MyNewCloudBot",
|
||||||
"user": "cloudbot",
|
"user": "cloudbot",
|
||||||
"realname": "CloudBot - http://j.mp/uS5Cvx",
|
"realname": "CloudBot - http://git.io/cloudbot",
|
||||||
"nickserv_password": "",
|
"nickserv_password": "",
|
||||||
"channels": ["#channel"],
|
"channels": ["#cloudbot"],
|
||||||
|
"invitejoin": true,
|
||||||
|
"autorejoin": false,
|
||||||
"command_prefix": ".",
|
"command_prefix": ".",
|
||||||
"stayalive": "",
|
"stayalive": false,
|
||||||
"stayalive_delay": "20"
|
"stayalive_delay": 20
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"disabled_plugins": [],
|
"disabled_plugins": [],
|
||||||
|
@ -36,23 +38,28 @@ if not os.path.exists('config'):
|
||||||
"bitly_api": "INSERT API KEY FROM bitly.com HERE",
|
"bitly_api": "INSERT API KEY FROM bitly.com HERE",
|
||||||
"wolframalpha": "INSERT API KEY FROM wolframalpha.com HERE",
|
"wolframalpha": "INSERT API KEY FROM wolframalpha.com HERE",
|
||||||
"lastfm": "INSERT API KEY FROM lastfm HERE",
|
"lastfm": "INSERT API KEY FROM lastfm HERE",
|
||||||
"mc_user": "INSERT MINECRAFT USERNAME HERE (used to check login servers in mctools.py)",
|
"mc_user": "INSERT MINECRAFT USERNAME HERE",
|
||||||
"mc_pass": "INSERT MINECRAFT PASSWORD HERE (used to check login servers in mctools.py)"
|
"mc_pass": "INSERT MINECRAFT PASSWORD HERE"
|
||||||
|
},
|
||||||
|
"plugins":
|
||||||
|
{
|
||||||
|
"factoids":
|
||||||
|
{
|
||||||
|
"prefix": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"censored_strings":
|
"censored_strings":
|
||||||
[
|
[
|
||||||
"DCC SEND",
|
"mypass",
|
||||||
"1nj3ct",
|
"mysecret"
|
||||||
"thewrestlinggame",
|
|
||||||
"startkeylogger",
|
|
||||||
"hybux",
|
|
||||||
"\\0",
|
|
||||||
"\\x01",
|
|
||||||
"!coz",
|
|
||||||
"!tell /x"
|
|
||||||
],
|
],
|
||||||
"admins": []
|
"admins": ["myname"]
|
||||||
}''') + '\n')
|
}''') + '\n')
|
||||||
|
print "Config generated!"
|
||||||
|
print "Please edit the config now!"
|
||||||
|
print "For help, see http://git.io/cloudbotwiki"
|
||||||
|
print "Thank you for using CloudBot!"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
|
@ -63,7 +70,8 @@ def config():
|
||||||
bot.config = json.load(open('config'))
|
bot.config = json.load(open('config'))
|
||||||
bot._config_mtime = config_mtime
|
bot._config_mtime = config_mtime
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
print 'ERROR: malformed config!', e
|
print 'error: malformed config', e
|
||||||
|
|
||||||
|
|
||||||
bot._config_mtime = 0
|
bot._config_mtime = 0
|
||||||
|
|
||||||
|
|
1
core/db.py
Normal file → Executable file
1
core/db.py
Normal file → Executable file
|
@ -4,6 +4,7 @@ import thread
|
||||||
|
|
||||||
threaddbs = {}
|
threaddbs = {}
|
||||||
|
|
||||||
|
|
||||||
def get_db_connection(conn, name=''):
|
def get_db_connection(conn, name=''):
|
||||||
"returns an sqlite3 connection to a persistent database"
|
"returns an sqlite3 connection to a persistent database"
|
||||||
|
|
||||||
|
|
4
core/irc.py
Normal file → Executable file
4
core/irc.py
Normal file → Executable file
|
@ -147,8 +147,8 @@ class IRC(object):
|
||||||
self.set_pass(self.conf.get('server_password'))
|
self.set_pass(self.conf.get('server_password'))
|
||||||
self.set_nick(self.nick)
|
self.set_nick(self.nick)
|
||||||
self.cmd("USER",
|
self.cmd("USER",
|
||||||
[conf.get('user', 'skybot'), "3", "*", conf.get('realname',
|
[conf.get('user', 'cloudbot'), "3", "*", conf.get('realname',
|
||||||
'Python bot - http://github.com/rmmh/skybot')])
|
'CloudBot - http://git.io/cloudbot')])
|
||||||
|
|
||||||
def parse_loop(self):
|
def parse_loop(self):
|
||||||
while True:
|
while True:
|
||||||
|
|
2
core/main.py
Normal file → Executable file
2
core/main.py
Normal file → Executable file
|
@ -157,7 +157,7 @@ def main(conn, out):
|
||||||
prefix = r'^(?:[' + commandprefix + ']|'
|
prefix = r'^(?:[' + commandprefix + ']|'
|
||||||
|
|
||||||
command_re = prefix + inp.conn.nick
|
command_re = prefix + inp.conn.nick
|
||||||
command_re += r'[:]+\s+)(\w+)(?:$|\s+)(.*)'
|
command_re += r'[,;:]+\s+)(\w+)(?:$|\s+)(.*)'
|
||||||
|
|
||||||
m = re.match(command_re, inp.lastparam)
|
m = re.match(command_re, inp.lastparam)
|
||||||
|
|
||||||
|
|
0
core/reload.py
Normal file → Executable file
0
core/reload.py
Normal file → Executable file
0
disabled_plugins/antiflood.py
Normal file → Executable file
0
disabled_plugins/antiflood.py
Normal file → Executable file
0
disabled_plugins/rottentomatoes.py
Normal file → Executable file
0
disabled_plugins/rottentomatoes.py
Normal file → Executable file
0
disabled_plugins/suggest.py
Normal file → Executable file
0
disabled_plugins/suggest.py
Normal file → Executable file
|
@ -1,192 +0,0 @@
|
||||||
import htmlentitydefs
|
|
||||||
import re
|
|
||||||
|
|
||||||
from util import hook, http
|
|
||||||
|
|
||||||
########### from http://effbot.org/zone/re-sub.htm#unescape-html #############
|
|
||||||
|
|
||||||
|
|
||||||
def unescape(text):
|
|
||||||
def fixup(m):
|
|
||||||
text = m.group(0)
|
|
||||||
if text[:2] == "&#":
|
|
||||||
# character reference
|
|
||||||
try:
|
|
||||||
if text[:3] == "&#x":
|
|
||||||
return unichr(int(text[3:-1], 16))
|
|
||||||
else:
|
|
||||||
return unichr(int(text[2:-1]))
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# named entity
|
|
||||||
try:
|
|
||||||
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
return text # leave as is
|
|
||||||
|
|
||||||
return re.sub("&#?\w+;", fixup, text)
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
|
|
||||||
|
|
||||||
def goog_trans(text, slang, tlang):
|
|
||||||
url = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&key=ABQIAAAAGjLiqTxkFw7F24ITXc4bNRS04yDz5pgaUTdxja2Sk3UoWlae7xTXom3fBzER6Upo8jfzcTtvz-8ebQ'
|
|
||||||
parsed = http.get_json(url, q=text, langpair=(slang + '|' + tlang))
|
|
||||||
if not 200 <= parsed['responseStatus'] < 300:
|
|
||||||
raise IOError('error with the translation server: %d: %s' % (
|
|
||||||
parsed['responseStatus'], parsed['responseDetails']))
|
|
||||||
if not slang:
|
|
||||||
return unescape('(%(detectedSourceLanguage)s) %(translatedText)s' %
|
|
||||||
(parsed['responseData']))
|
|
||||||
return unescape(parsed['responseData']['translatedText'])
|
|
||||||
|
|
||||||
|
|
||||||
def match_language(fragment):
|
|
||||||
fragment = fragment.lower()
|
|
||||||
for short, _ in lang_pairs:
|
|
||||||
if fragment in short.lower().split():
|
|
||||||
return short.split()[0]
|
|
||||||
|
|
||||||
for short, full in lang_pairs:
|
|
||||||
if fragment in full.lower():
|
|
||||||
return short.split()[0]
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
|
||||||
def translate(inp):
|
|
||||||
'.translate [source language [target language]] <sentence> -- translates' \
|
|
||||||
' <sentence> from source language (default autodetect) to target' \
|
|
||||||
' language (default English) using Google Translate'
|
|
||||||
return "Due to Google deprecating the translation API, this command is no longer available :("
|
|
||||||
|
|
||||||
args = inp.split(' ', 2)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if len(args) >= 2:
|
|
||||||
sl = match_language(args[0])
|
|
||||||
if not sl:
|
|
||||||
return goog_trans(inp, '', 'en')
|
|
||||||
if len(args) >= 3:
|
|
||||||
tl = match_language(args[1])
|
|
||||||
if not tl:
|
|
||||||
if sl == 'en':
|
|
||||||
return 'unable to determine desired target language'
|
|
||||||
return goog_trans(args[1] + ' ' + args[2], sl, 'en')
|
|
||||||
return goog_trans(args[2], sl, tl)
|
|
||||||
return goog_trans(inp, '', 'en')
|
|
||||||
except IOError, e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
|
|
||||||
languages = 'ja fr de ko ru zh'.split()
|
|
||||||
language_pairs = zip(languages[:-1], languages[1:])
|
|
||||||
|
|
||||||
|
|
||||||
def babel_gen(inp):
|
|
||||||
for language in languages:
|
|
||||||
inp = inp.encode('utf8')
|
|
||||||
trans = goog_trans(inp, 'en', language).encode('utf8')
|
|
||||||
inp = goog_trans(trans, language, 'en')
|
|
||||||
yield language, trans, inp
|
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
|
||||||
def babel(inp):
|
|
||||||
".babel <sentence> -- translates <sentence> through multiple languages"
|
|
||||||
return "Due to Google deprecating the translation API, this command is no longer available :("
|
|
||||||
|
|
||||||
try:
|
|
||||||
return list(babel_gen(inp))[-1][2]
|
|
||||||
except IOError, e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
|
||||||
def babelext(inp):
|
|
||||||
".babelext <sentence> -- like .babel, but with more detailed output"
|
|
||||||
|
|
||||||
return "Due to Google deprecating the translation API, this command is no longer available :("
|
|
||||||
|
|
||||||
try:
|
|
||||||
babels = list(babel_gen(inp))
|
|
||||||
except IOError, e:
|
|
||||||
return e
|
|
||||||
|
|
||||||
out = u''
|
|
||||||
for lang, trans, text in babels:
|
|
||||||
out += '%s:"%s", ' % (lang, text.decode('utf8'))
|
|
||||||
|
|
||||||
out += 'en:"' + babels[-1][2].decode('utf8') + '"'
|
|
||||||
|
|
||||||
if len(out) > 300:
|
|
||||||
out = out[:150] + ' ... ' + out[-150:]
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
lang_pairs = [
|
|
||||||
("no", "Norwegian"),
|
|
||||||
("it", "Italian"),
|
|
||||||
("ht", "Haitian Creole"),
|
|
||||||
("af", "Afrikaans"),
|
|
||||||
("sq", "Albanian"),
|
|
||||||
("ar", "Arabic"),
|
|
||||||
("hy", "Armenian"),
|
|
||||||
("az", "Azerbaijani"),
|
|
||||||
("eu", "Basque"),
|
|
||||||
("be", "Belarusian"),
|
|
||||||
("bg", "Bulgarian"),
|
|
||||||
("ca", "Catalan"),
|
|
||||||
("zh-CN zh", "Chinese"),
|
|
||||||
("hr", "Croatian"),
|
|
||||||
("cs", "Czech"),
|
|
||||||
("da", "Danish"),
|
|
||||||
("nl", "Dutch"),
|
|
||||||
("en", "English"),
|
|
||||||
("et", "Estonian"),
|
|
||||||
("tl", "Filipino"),
|
|
||||||
("fi", "Finnish"),
|
|
||||||
("fr", "French"),
|
|
||||||
("gl", "Galician"),
|
|
||||||
("ka", "Georgian"),
|
|
||||||
("de", "German"),
|
|
||||||
("el", "Greek"),
|
|
||||||
("ht", "Haitian Creole"),
|
|
||||||
("iw", "Hebrew"),
|
|
||||||
("hi", "Hindi"),
|
|
||||||
("hu", "Hungarian"),
|
|
||||||
("is", "Icelandic"),
|
|
||||||
("id", "Indonesian"),
|
|
||||||
("ga", "Irish"),
|
|
||||||
("it", "Italian"),
|
|
||||||
("ja jp jpn", "Japanese"),
|
|
||||||
("ko", "Korean"),
|
|
||||||
("lv", "Latvian"),
|
|
||||||
("lt", "Lithuanian"),
|
|
||||||
("mk", "Macedonian"),
|
|
||||||
("ms", "Malay"),
|
|
||||||
("mt", "Maltese"),
|
|
||||||
("no", "Norwegian"),
|
|
||||||
("fa", "Persian"),
|
|
||||||
("pl", "Polish"),
|
|
||||||
("pt", "Portuguese"),
|
|
||||||
("ro", "Romanian"),
|
|
||||||
("ru", "Russian"),
|
|
||||||
("sr", "Serbian"),
|
|
||||||
("sk", "Slovak"),
|
|
||||||
("sl", "Slovenian"),
|
|
||||||
("es", "Spanish"),
|
|
||||||
("sw", "Swahili"),
|
|
||||||
("sv", "Swedish"),
|
|
||||||
("th", "Thai"),
|
|
||||||
("tr", "Turkish"),
|
|
||||||
("uk", "Ukrainian"),
|
|
||||||
("ur", "Urdu"),
|
|
||||||
("vi", "Vietnamese"),
|
|
||||||
("cy", "Welsh"),
|
|
||||||
("yi", "Yiddish")
|
|
||||||
]
|
|
0
disabled_plugins/urlhistory.py
Normal file → Executable file
0
disabled_plugins/urlhistory.py
Normal file → Executable file
196
disabled_plugins/wrapper.old
Executable file
196
disabled_plugins/wrapper.old
Executable file
|
@ -0,0 +1,196 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Bot Wrapper by neersighted
|
||||||
|
|
||||||
|
# Import required modules
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Files
|
||||||
|
configfile = os.path.isfile("./config")
|
||||||
|
botfile = os.path.isfile("./bot.py")
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
nocol = "\033[1;m"
|
||||||
|
red = "\033[1;31m"
|
||||||
|
green = "\033[1;32m"
|
||||||
|
|
||||||
|
# Messages
|
||||||
|
firstrun = "Welclome to your first run of: "
|
||||||
|
usage = "usage: ./cloudbot {start|stop|restart|status}"
|
||||||
|
iusage = "{1|start} {2|stop} {3|restart} {4|status} {5|exit}"
|
||||||
|
quit = "Thanks for using CloudBot!"
|
||||||
|
|
||||||
|
error1 = red + "Neither screen nor daemon is installed! "\
|
||||||
|
"This program cannot run! {ERROR 1}" + nocol
|
||||||
|
error2 = red + "Could not find bot.py! Are you in the wrong folder? "\
|
||||||
|
"{ERROR 2}" + nocol
|
||||||
|
error3 = red + "Invalid choice, exiting! {ERROR 3}" + nocol
|
||||||
|
error4 = red + "Program killed by user! {ERROR 4}" + nocol
|
||||||
|
error5 = red + "Invalid backend in config! (Or, backend not installed)"\
|
||||||
|
" {ERROR 5}" + nocol
|
||||||
|
error6 = red + "Author error! We be derpin'! {ERROR 6}" + nocol
|
||||||
|
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
pwd = os.getcwd()
|
||||||
|
clearlog = ": > ./bot.log"
|
||||||
|
|
||||||
|
start = "echo " + "'" + error1 + "'"
|
||||||
|
stop = "echo " + "'" + error1 + "'"
|
||||||
|
restart = "echo " + "'" + error1 + "'"
|
||||||
|
pid = "echo 'Cannot get pid'"
|
||||||
|
|
||||||
|
daemonstart = "daemon -r -n cloudbot -O " + pwd + \
|
||||||
|
"/bot.log python " + pwd + "/bot.py"
|
||||||
|
daemonstop = "daemon -n cloudbot --stop"
|
||||||
|
daemonrestart = "./cloudbot stop > /dev/null 2>&1 && ./cloudbot start > /dev/null 2>&1"
|
||||||
|
daemonpid = "pidof /usr/bin/daemon"
|
||||||
|
|
||||||
|
screenstart = "screen -d -m -S cloudbot -t cloudbot python " + pwd +\
|
||||||
|
"/bot.py > " + pwd + "/bot.log 2>&1"
|
||||||
|
screenstop = "kill `pidof /usr/bin/screen`"
|
||||||
|
screenrestart = "./cloudbot stop > /dev/null 2>&1 && ./cloudbot start > /dev/null 2>&1"
|
||||||
|
screenpid = "pidof /usr/bin/screen"
|
||||||
|
|
||||||
|
# Checks
|
||||||
|
if configfile:
|
||||||
|
try:
|
||||||
|
config = json.load(open('config'))
|
||||||
|
command = ":"
|
||||||
|
except ValueError, e:
|
||||||
|
print 'error: malformed config', e
|
||||||
|
else:
|
||||||
|
config = False
|
||||||
|
command = "python bot.py"
|
||||||
|
|
||||||
|
daemoncheck = subprocess.check_output("locate /usr/bin/daemon", shell=True)
|
||||||
|
daemon = re.match(r'^/usr/bin/daemon$', daemoncheck)
|
||||||
|
|
||||||
|
screencheck = subprocess.check_output("locate /usr/bin/screen", shell=True)
|
||||||
|
screen = re.match(r'^/usr/bin/screen$', screencheck)
|
||||||
|
|
||||||
|
if configfile:
|
||||||
|
backend = config.get("wrapper", {}).get("backend", "daemon")
|
||||||
|
daemonloc = config.get("wrapper", {}).get("daemonloc", "/usr/bin/daemon")
|
||||||
|
screenloc = config.get("wrapper", {}).get("screenloc", "/usr/bin/screen")
|
||||||
|
else:
|
||||||
|
backend = False
|
||||||
|
daemonloc = "/usr/bin/daemon"
|
||||||
|
screenloc = "/usr/bin/screen"
|
||||||
|
|
||||||
|
try:
|
||||||
|
runningcheck = subprocess.check_output("ps ax|grep cloudbot|"\
|
||||||
|
"grep -v grep|grep -v ./cloudbot", shell=True)
|
||||||
|
running = re.match(r'^[1-9]+', runningcheck)
|
||||||
|
except (subprocess.CalledProcessError):
|
||||||
|
running = False
|
||||||
|
|
||||||
|
# Set commands
|
||||||
|
if (backend == "daemon"):
|
||||||
|
if daemon:
|
||||||
|
start = daemonstart
|
||||||
|
stop = daemonstop
|
||||||
|
restart = daemonrestart
|
||||||
|
pid = daemonpid
|
||||||
|
else:
|
||||||
|
print error5
|
||||||
|
exit
|
||||||
|
elif (backend == "screen"):
|
||||||
|
if screen:
|
||||||
|
start = screenstart
|
||||||
|
stop = screenstop
|
||||||
|
restart = screenrestart
|
||||||
|
pid = screenpid
|
||||||
|
else:
|
||||||
|
print error5
|
||||||
|
exit
|
||||||
|
elif (backend == False):
|
||||||
|
print firstrun
|
||||||
|
else:
|
||||||
|
print error5
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Fancy banner
|
||||||
|
print " ______ __ ______ __ __ "\
|
||||||
|
" _______ .______ ______ .___________."
|
||||||
|
print " / || | / __ \ | | | | "\
|
||||||
|
"| \ | _ \ / __ \ | |"
|
||||||
|
print "| ,----'| | | | | | | | | | "\
|
||||||
|
"| .--. || |_) | | | | | `---| |----`"
|
||||||
|
print "| | | | | | | | | | | | "\
|
||||||
|
"| | | || _ < | | | | | | "
|
||||||
|
print "| `----.| `----.| `--' | | `--' | "\
|
||||||
|
"| '--' || |_) | | `--' | | | "
|
||||||
|
print " \______||_______| \______/ \______/ "\
|
||||||
|
"|_______/ |______/ \______/ |__| "
|
||||||
|
print "http://git.io/cloudbot "\
|
||||||
|
" by lukeroge"
|
||||||
|
|
||||||
|
# Read arguments/turn interactive
|
||||||
|
try:
|
||||||
|
if (len(sys.argv) > 1):
|
||||||
|
read = 0
|
||||||
|
else:
|
||||||
|
sys.argv = "interactive"
|
||||||
|
print iusage
|
||||||
|
read = int(raw_input("Please choose a option: "))
|
||||||
|
|
||||||
|
if (sys.argv[1] == "start") or (read == 1):
|
||||||
|
if running:
|
||||||
|
print "Bot is already running, cannot start!"
|
||||||
|
else:
|
||||||
|
command = start
|
||||||
|
print "Starting... (" + backend + ")"
|
||||||
|
elif (sys.argv[1] == "stop") or (read == 2):
|
||||||
|
if running:
|
||||||
|
command = stop
|
||||||
|
print "Stopping... (" + backend + ")"
|
||||||
|
else:
|
||||||
|
print "Bot is not running, cannot stop!"
|
||||||
|
elif (sys.argv[1] == "restart") or (read == 3):
|
||||||
|
if running:
|
||||||
|
command = restart
|
||||||
|
print "Restarting... (" + backend + ")"
|
||||||
|
else:
|
||||||
|
print "Bot is not running, cannot restart!"
|
||||||
|
elif (sys.argv[1] == "status") or (read == 4):
|
||||||
|
if running:
|
||||||
|
command = pid
|
||||||
|
print green + "Bot is running! " + nocol
|
||||||
|
else:
|
||||||
|
print red + "Bot is not running! " + nocol
|
||||||
|
elif (sys.argv[1] == "clear"):
|
||||||
|
command = clearlog
|
||||||
|
elif (sys.argv[1] == "exit") or (read == 5):
|
||||||
|
exit
|
||||||
|
elif (sys.argv[1] == "interactive"):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print usage
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Pretify errors
|
||||||
|
except (TypeError, ValueError), e:
|
||||||
|
print error3
|
||||||
|
exit
|
||||||
|
except (KeyboardInterrupt), e:
|
||||||
|
print error4
|
||||||
|
exit
|
||||||
|
except (NameError, SyntaxError), e:
|
||||||
|
print error6
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Check for bot files
|
||||||
|
if botfile:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print error2
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Call command
|
||||||
|
subprocess.call(command, shell=True)
|
||||||
|
print quit
|
||||||
|
exit
|
26
docs/bots
26
docs/bots
|
@ -1,26 +0,0 @@
|
||||||
Other bots we should "borrow" ideas from:
|
|
||||||
|
|
||||||
supybot http://supybot.com/
|
|
||||||
- horribly bloated plugin structure, each plugin has its own directory and 4 files (unit testing for plugins what)
|
|
||||||
|
|
||||||
phenny http://inamidst.com/phenny/
|
|
||||||
- inspiration for skybot, too much magic and not easy enough to change
|
|
||||||
|
|
||||||
pyfibot http://code.google.com/p/pyfibot/
|
|
||||||
- interesting, but lots of magic
|
|
||||||
|
|
||||||
rbot http://linuxbrit.co.uk/rbot/
|
|
||||||
- ruby
|
|
||||||
- lots of plugins
|
|
||||||
|
|
||||||
pyirc http://www.k-pdt.net/pyirc/
|
|
||||||
- very simple, not multithreaded
|
|
||||||
- poor use of regexes, CloudBot has much better parsing, but it implements many more irc control codes
|
|
||||||
- can convert irc colors to vt100 escape codes -- should implement this
|
|
||||||
- autoreconnect
|
|
||||||
|
|
||||||
pybot http://labix.org/pybot
|
|
||||||
- can handle multiple servers, but not multithreaded
|
|
||||||
- ugly modules
|
|
||||||
- too many external dependencies
|
|
||||||
- attempt at NLP
|
|
|
@ -1,21 +0,0 @@
|
||||||
GOALS:
|
|
||||||
* simplicity
|
|
||||||
* as little boilerplate and magic as possible
|
|
||||||
* multithreaded dispatch
|
|
||||||
|
|
||||||
plugins are located in plugins/
|
|
||||||
|
|
||||||
input:
|
|
||||||
|
|
||||||
nick -- string, the nickname of whoever sent the message
|
|
||||||
channel -- string, the channel the message was sent on. Equal to nick if it's a private message.
|
|
||||||
msg -- string, the line that was sent
|
|
||||||
raw -- string, the raw full line that was sent
|
|
||||||
re -- the result of doing re.match(hook, msg)
|
|
||||||
|
|
||||||
attributes and methods of bot:
|
|
||||||
|
|
||||||
say(msg): obvious
|
|
||||||
reply(msg): say(input.nick + ": " + msg)
|
|
||||||
msg(target, msg): sends msg to target
|
|
||||||
(other irc commands, like mode, topic, etc)
|
|
29
docs/plugins
29
docs/plugins
|
@ -1,29 +0,0 @@
|
||||||
All plugins need to 'from util import hook' if they want to be callable.
|
|
||||||
|
|
||||||
There are three ways to set when a plugin is called using
|
|
||||||
decorators.
|
|
||||||
|
|
||||||
@hook.command causes it to be callable using normal command
|
|
||||||
syntax; an argument will register it under that name (so if my function is
|
|
||||||
called foo and I use @hook.command, .foo will work; if I use
|
|
||||||
@hook.command("bar"), .bar will work but not .foo). The first argument, inp,
|
|
||||||
will be the text that occurs after the command. (e.g., "bar" in ".foo bar").
|
|
||||||
|
|
||||||
@hook.regex takes an argument corresponding to the regex string (not the
|
|
||||||
compiled regex), followed by optional flags. It will attempt to match the regex
|
|
||||||
on all inputs; if so, the hooked function will be called with the match object.
|
|
||||||
|
|
||||||
@hook.event requires a parameter; if it's '*", it will trigger on every line. If
|
|
||||||
it's 'PRIVMSG', it'll trigger on only actual lines of chat (not
|
|
||||||
nick-changes). The first argument in these cases will be a two-element list of
|
|
||||||
the form ["#channel", "text"]; I don't know what it's like for NICK or other
|
|
||||||
'commands'.
|
|
||||||
|
|
||||||
@hook.singlethread indicates that the command should run in its own thread; this
|
|
||||||
means that you can't use the existing database connection object!
|
|
||||||
|
|
||||||
In addition to the standard argument, plugins can take other arguments; db is
|
|
||||||
the database object; input corresponds to the triggering line of text, and bot
|
|
||||||
is the bot itself.
|
|
||||||
|
|
||||||
TODO: describe what can be done with db, input, and bot.
|
|
|
@ -1,914 +0,0 @@
|
||||||
Ed. Note: The following note is from the author's original email
|
|
||||||
announcing this CTCP specification file. All of this came after the
|
|
||||||
original RFC 1459 for the IRC protocol. -Jolo
|
|
||||||
|
|
||||||
From: ben@gnu.ai.mit.edu
|
|
||||||
Subject: REVISED AND UPDATED CTCP SPECIFICATION
|
|
||||||
Date: Fri, 12 Aug 94 00:21:54 edt
|
|
||||||
|
|
||||||
As part of documenting the ZenIRC client, I expanded, revised, and
|
|
||||||
merged two text files that have been around on IRC archive sites for
|
|
||||||
some time: ctcp.doc, and dcc.protocol. The file "ctcp.doc" by Klaus
|
|
||||||
Zeuge described the basic CTCP protocol, and most of the CTCP commands
|
|
||||||
other than DCC. The file Troy Rollo wrote, "dcc.protocol", contained
|
|
||||||
a description of the CTCP DCC messages as well as the protocols used
|
|
||||||
by DCC CHAT and DCC file transfers. I have merged the two documents to
|
|
||||||
produce this one, edited them for clarity, and expanded on them where I
|
|
||||||
found them unclear while implementing CTCP in the ZenIRC client.
|
|
||||||
|
|
||||||
--Ben
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
The Client-To-Client Protocol (CTCP)
|
|
||||||
|
|
||||||
Klaus Zeuge <sojge@Minsk.DoCS.UU.SE>
|
|
||||||
Troy Rollo <troy@plod.cbme.unsw.oz.au>
|
|
||||||
Ben Mesander <ben@gnu.ai.mit.edu>
|
|
||||||
|
|
||||||
|
|
||||||
The Client-To-Client Protocol is meant to be used as a way to
|
|
||||||
1/ in general send structured data (such as graphics,
|
|
||||||
voice and different font information) between users
|
|
||||||
clients, and in a more specific case:
|
|
||||||
2/ place a query to a users client and getting an answer.
|
|
||||||
|
|
||||||
*****************************************
|
|
||||||
BASIC PROTOCOL BETWEEN CLIENTS AND SERVER
|
|
||||||
*****************************************
|
|
||||||
|
|
||||||
Characters between an Internet Relay Chat (IRC) client and server are
|
|
||||||
8 bit bytes (also known as octets) and can have numeric values from
|
|
||||||
octal \000 to \377 inclusive (0 to 255 decimal). Some characters are
|
|
||||||
special:
|
|
||||||
|
|
||||||
CHARS ::= '\000' .. '\377'
|
|
||||||
NUL ::= '\000'
|
|
||||||
NL ::= '\n'
|
|
||||||
CR ::= '\r'
|
|
||||||
|
|
||||||
Note: `\' followed by three digits is used to denote an octal value in this
|
|
||||||
paper. `\' followed by an alphabetic character is used to denote a C
|
|
||||||
language style special character, and `..' denotes a range of characters.
|
|
||||||
|
|
||||||
A line sent to a server, or received from a server (here called "low
|
|
||||||
level messages") consist or zero or more octets (expcept NUL, NL or
|
|
||||||
CR) with either a NL or CR appended.
|
|
||||||
|
|
||||||
L-CHARS ::= '\001' .. '\011' | '\013' | '\014' |
|
|
||||||
'\016' .. '\377'
|
|
||||||
L-LINE ::= L-CHARS* CR LF
|
|
||||||
|
|
||||||
Note: The `*' is used here to denote "zero or more of the preceding class of
|
|
||||||
characters", and the `|' is used to denote alternation.
|
|
||||||
|
|
||||||
A NUL is never sent to the server.
|
|
||||||
|
|
||||||
*****************
|
|
||||||
LOW LEVEL QUOTING
|
|
||||||
*****************
|
|
||||||
|
|
||||||
Even though messages to and from IRC servers cannot contain NUL, NL,
|
|
||||||
or CR, it still might be desirable to send ANY character (in so called
|
|
||||||
"middle level messages") between clients. In order for this to be
|
|
||||||
possible, those three characters have to be quoted. Therefore a quote
|
|
||||||
character is needed. Of course, the quote character itself has to be
|
|
||||||
quoted too, since it is in-band.
|
|
||||||
|
|
||||||
M-QUOTE ::= '\020'
|
|
||||||
|
|
||||||
(Ie a CNTRL/P).
|
|
||||||
|
|
||||||
When sending a middle level message, if there is a character in the
|
|
||||||
set { NUL, NL, CR, M-QUOTE } present in the message, that character is
|
|
||||||
replaced by a two character sequence according to the following table:
|
|
||||||
|
|
||||||
NUL --> M-QUOTE '0'
|
|
||||||
NL --> M-QUOTE 'n'
|
|
||||||
CR --> M-QUOTE 'r'
|
|
||||||
M-QUOTE --> M-QUOTE M-QUOTE
|
|
||||||
|
|
||||||
When receiving a low level message, if there is a M-QUOTE, look at the
|
|
||||||
next character, and replace those two according to the following table
|
|
||||||
to get the corresponding middle level message:
|
|
||||||
|
|
||||||
M-QUOTE '0' --> NUL
|
|
||||||
M-QUOTE 'n' --> NL
|
|
||||||
M-QUOTE 'r' --> CR
|
|
||||||
M-QUOTE M-QUOTE --> M-QUOTE
|
|
||||||
|
|
||||||
If the character following M-QUOTE is not any of the listed
|
|
||||||
characters, that is an error, so drop the M-QUOTE character from the
|
|
||||||
message, optionally warning the user about it. For example, a string
|
|
||||||
'x' M-QUOTE 'y' 'z' from a server dequotes into 'x 'y' 'z'.
|
|
||||||
|
|
||||||
Before low level quoting, a message to the server (and in the opposite
|
|
||||||
direction: after low level dequoting, a message from the server) looks
|
|
||||||
like:
|
|
||||||
|
|
||||||
M-LINE ::= CHARS*
|
|
||||||
|
|
||||||
***********
|
|
||||||
TAGGED DATA
|
|
||||||
***********
|
|
||||||
|
|
||||||
To send both extended data and query/reply pairs between clients, an
|
|
||||||
extended data format is needed. The extended data are sent in the text
|
|
||||||
part of a middle level message (and after low level quoting, in the
|
|
||||||
text part of the low level message).
|
|
||||||
|
|
||||||
To send extended data inside the middle level message, we need some
|
|
||||||
way to delimit it. This is done by starting and ending extended data
|
|
||||||
with a delimiter character, defined as:
|
|
||||||
|
|
||||||
X-DELIM ::= '\001'
|
|
||||||
|
|
||||||
As both the starting and ending delimiter looks the same, the first
|
|
||||||
X-DELIM is called the odd delimiter, and the one that follows, the
|
|
||||||
even delimiter. The next one after that, an odd delimiter, then and
|
|
||||||
even, and so on.
|
|
||||||
|
|
||||||
When data are quoted (and conversely, before being dequoted) any number
|
|
||||||
of characters of any kind except X-DELIM can be used in the extended
|
|
||||||
data inside the X-DELIM pair.
|
|
||||||
|
|
||||||
X-CHR ::= '\000' | '\002' .. '\377'
|
|
||||||
|
|
||||||
An extended message is either empty (nothing between the odd and even
|
|
||||||
delimiter), has one or more non-space characters (any character but
|
|
||||||
'\040') or has one or more non-space characters followed by a space
|
|
||||||
followed by zero or more characters.
|
|
||||||
|
|
||||||
X-N-AS ::= '\000' | '\002' .. '\037' | '\041' .. '\377'
|
|
||||||
SPC ::= '\040'
|
|
||||||
X-MSG ::= | X-N-AS+ | X-N-AS+ SPC X-CHR*
|
|
||||||
|
|
||||||
Note: Here `+' is used to denote "one or more of the previous class of
|
|
||||||
characters", and `*' is used to denote "zero or more of the previous
|
|
||||||
class of characters".
|
|
||||||
|
|
||||||
The characters up until the first SPC (or if no SPC, all of the X-MSG)
|
|
||||||
is called the tag of the extended message. The tag is used to denote
|
|
||||||
what kind of extended data is used.
|
|
||||||
|
|
||||||
The tag can be *any* string of characters, and if it contains
|
|
||||||
alphabetics, it is case sensitive, so upper and lower case matters.
|
|
||||||
|
|
||||||
Extended data is only valid in PRIVMSG and NOTICE commands. If the
|
|
||||||
extended data is a reply to a query, it is sent in a NOTICE, otherwise
|
|
||||||
it is sent in a PRIVMSG. Both PRIVMSG and NOTICE to a user and to a
|
|
||||||
channel may contain extended data.
|
|
||||||
|
|
||||||
The text part of a PRIVMSG or NOTICE might contain zero or more
|
|
||||||
extended messages, intermixed with zero or more chunks of non-extended
|
|
||||||
data.
|
|
||||||
|
|
||||||
******************
|
|
||||||
CTCP LEVEL QUOTING
|
|
||||||
******************
|
|
||||||
|
|
||||||
In order to be able to send the delimiter X-DELIM inside an extended
|
|
||||||
data message, it has to be quoted. This introduces another quote
|
|
||||||
character (which differs from the low level quote character so it
|
|
||||||
won't have to be quoted yet again).
|
|
||||||
|
|
||||||
X-QUOTE ::= '\134'
|
|
||||||
|
|
||||||
(a back slash - `\').
|
|
||||||
|
|
||||||
When quoting on the CTCP level, only the actual CTCP message (extended
|
|
||||||
data, queries, replies) are quoted. This enables users to actually
|
|
||||||
send X-QUOTE characters at will. The following translations should be
|
|
||||||
used:
|
|
||||||
|
|
||||||
X-DELIM --> X-QUOTE 'a'
|
|
||||||
X-QUOTE --> X-QUOTE X-QUOTE
|
|
||||||
|
|
||||||
and when dequoting on the CTCP level, only CTCP messages are dequoted
|
|
||||||
whereby the following table is used.
|
|
||||||
|
|
||||||
X-QUOTE 'a' --> X-DELIM
|
|
||||||
X-QUOTE X-QUOTE --> X-QUOTE
|
|
||||||
|
|
||||||
If an X-QUOTE is seen with a character following it other than the
|
|
||||||
ones above, that is an error and the X-QUOTE character should be
|
|
||||||
dropped. For example the CTCP-quoted string 'x' X-QUOTE 'y' 'z'
|
|
||||||
becomes after dequoting, the three character string 'x' 'y' 'z'.
|
|
||||||
|
|
||||||
If a X-DELIM is found outside a CTCP message, the message will contain
|
|
||||||
the X-DELIM. (This should only happen with the last X-DELIM when there
|
|
||||||
are an odd number of X-DELIM's in a middle level message.)
|
|
||||||
|
|
||||||
****************
|
|
||||||
QUOTING EXAMPLES
|
|
||||||
****************
|
|
||||||
|
|
||||||
There are three levels of messages. The highest level (H) is the text
|
|
||||||
on the user-to-client level. The middle layer (M) is on the level
|
|
||||||
where CTCP quoting has been applied to the H-level message. The lowest
|
|
||||||
level (L) is on the client-to-server level, where low level quoting
|
|
||||||
has been applied to the M-level message.
|
|
||||||
|
|
||||||
The following relations are true, with lowQuote(message) being a
|
|
||||||
function doing the low level quoting, lowDequote(message) the low
|
|
||||||
level dequoting function, ctcpQuote(message) the CTCP level quoting
|
|
||||||
function, ctcpDequote(message) the CTCP level dequoting function, and
|
|
||||||
ctcpExtract(message) the function which removes all CTCP messages from
|
|
||||||
a message:
|
|
||||||
|
|
||||||
L = lowQuote(M)
|
|
||||||
M = ctcpDequote(L)
|
|
||||||
|
|
||||||
M = ctcpQuote(H)
|
|
||||||
H = ctcpDequote(ctcpExtract(M))
|
|
||||||
|
|
||||||
When sending a CTCP message embedded in normal text:
|
|
||||||
|
|
||||||
M = ctcpQuote(H1) || '\001' || ctcpQuote(X) || '\001' || ctcpQuote(H2)
|
|
||||||
|
|
||||||
Note: The operator || denotes string concatenation.
|
|
||||||
|
|
||||||
Of course, there might be zero or more normal text messages and zero
|
|
||||||
or more CTCP messages mixed.
|
|
||||||
|
|
||||||
- --- Example 1 -----------------------------------------------------------------
|
|
||||||
|
|
||||||
A user (called actor) wanting to send the string:
|
|
||||||
|
|
||||||
Hi there!\nHow are you?
|
|
||||||
|
|
||||||
to user victim, i.e. a message where the user has entered an inline
|
|
||||||
newline (how this is done, if at all, differs from client to client),
|
|
||||||
will result internaly in the client in the command:
|
|
||||||
|
|
||||||
PRIVMSG victim :Hi there!\nHow are you? \K?
|
|
||||||
|
|
||||||
which will be CTCP quoted into:
|
|
||||||
|
|
||||||
PRIVMSG victim :Hi there!\nHow are you? \\K?
|
|
||||||
|
|
||||||
which in turn will be low level quoted into:
|
|
||||||
|
|
||||||
PRIVMSG victim :Hi there!\020nHow are you? \\K?
|
|
||||||
|
|
||||||
and sent to the server after appending a newline at the end.
|
|
||||||
|
|
||||||
This will arrive on victim's side as:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Hi there!\020nHow are you? \\K?
|
|
||||||
|
|
||||||
(where the \\K would look similar to OK in SIS D47 et. al.) which after
|
|
||||||
low level dequoting becomes:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Hi there!\nHow are you? \\K?
|
|
||||||
|
|
||||||
and after CTCP dequoting:
|
|
||||||
|
|
||||||
:actom PRIVMSG victim :Hi there!\nHow are you? \K?
|
|
||||||
|
|
||||||
How this is displayed differs from client to client, but it suggested
|
|
||||||
that a line break should occour between the words "there" and "How".
|
|
||||||
|
|
||||||
- --- Example 2 -----------------------------------------------------------------
|
|
||||||
|
|
||||||
If actor's client wants to send the string "Emacs wins" this might
|
|
||||||
become the string "\n\t\big\020\001\000\\:" when being SED-encrypted
|
|
||||||
[SED is a simple encryption protocol between IRC clients implemented
|
|
||||||
with CTCP. I don't have any reference for it -- Ben] using some key,
|
|
||||||
so the client starts by CTCP-quoting this string into the string
|
|
||||||
"\n\t\big\020\\a\000\\\\:" and builds the M-level message:
|
|
||||||
|
|
||||||
PRIVMSG victim :\001SED \n\t\big\020\\a\000\\\\:\001
|
|
||||||
|
|
||||||
which after low level quoting becomes:
|
|
||||||
|
|
||||||
PRIVMSG victim :\001SED \020n\t\big\020\020\\a\0200\\\\:\001
|
|
||||||
|
|
||||||
which will be sent to the server, with a newline tacked on.
|
|
||||||
|
|
||||||
On victim's side, the string:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :\001SED \020n\t\big\020\020\\a\0200\\\\:\001
|
|
||||||
|
|
||||||
will be received from the server and low level dequoted into:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :\001SED \n\t\big\020\\a\000\\\\:\001
|
|
||||||
|
|
||||||
whereafter the string "\n\t\big\020\\a\000\\\\:" will be extracted
|
|
||||||
and first CTCP dequoted into "\n\t\big\020\001\000\\:" and then
|
|
||||||
SED decoded getting back "Emacs wins" when using the same key.
|
|
||||||
|
|
||||||
- --- Example 3 -----------------------------------------------------------------
|
|
||||||
|
|
||||||
If the user actor wants to query the USERINFO of user victim, and is
|
|
||||||
in the middle of a conversation, the client may decide to tack on
|
|
||||||
USERINFO request on the end of a normal text message. Let's say actor
|
|
||||||
wants to send the textmessage "Say hi to Ron\n\t/actor" and the CTCP
|
|
||||||
request "USERINFO" to victim:
|
|
||||||
|
|
||||||
PRIVMSG victim :Say hi to Ron\n\t/actor
|
|
||||||
|
|
||||||
plus:
|
|
||||||
|
|
||||||
USERINFO
|
|
||||||
|
|
||||||
which after CTCP quoting become:
|
|
||||||
|
|
||||||
PRIVMSG victim :Say hi to Ron\n\t/actor
|
|
||||||
|
|
||||||
plus:
|
|
||||||
|
|
||||||
USERINFO
|
|
||||||
|
|
||||||
which gets merged into:
|
|
||||||
|
|
||||||
PRIVMSG victim :Say hi to Ron\n\t/actor\001USERINFO\001
|
|
||||||
|
|
||||||
and after low level quoting:
|
|
||||||
|
|
||||||
PRIVMSG victim :Say hi to Ron\020n\t/actor\001USERINFO\001
|
|
||||||
|
|
||||||
and sent off to the server.
|
|
||||||
|
|
||||||
On victim's side, the message:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Say hi to Ron\020n\t/actor\001USERINFO\001
|
|
||||||
|
|
||||||
arrives. This gets low level dequoted into:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Say hi to Ron\n\t/actor\001USERINFO\001
|
|
||||||
|
|
||||||
and thereafter split up into:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Say hi to Ron\n\t/actor
|
|
||||||
|
|
||||||
plus:
|
|
||||||
|
|
||||||
USERINFO
|
|
||||||
|
|
||||||
After CTCP dequoting both, the message:
|
|
||||||
|
|
||||||
:actor PRIVMSG victim :Say hi to Ron\n\t/actor
|
|
||||||
|
|
||||||
gets displayed, while the CTCP command:
|
|
||||||
|
|
||||||
USERINFO
|
|
||||||
|
|
||||||
gets replied to. The reply might be:
|
|
||||||
|
|
||||||
USERINFO :CS student\n\001test\001
|
|
||||||
|
|
||||||
which gets CTCP quoted into:
|
|
||||||
|
|
||||||
USERINFO :CS student\n\\atest\\a
|
|
||||||
|
|
||||||
and sent in a NOTICE as it is a reply:
|
|
||||||
|
|
||||||
NOTICE actor :\001USERINFO :CS student\n\\atest\\a\001
|
|
||||||
|
|
||||||
and low level quoted into:
|
|
||||||
|
|
||||||
NOTICE actor :\001USERINFO :CS student\020n\\atest\\a\001
|
|
||||||
|
|
||||||
after which is it sent to victim's server.
|
|
||||||
|
|
||||||
When arriving on actor's side, the message:
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001USERINFO :CS student\020n\\atest\\a\001
|
|
||||||
|
|
||||||
gets low level dequoted into:
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001USERINFO :CS student\n\\atest\\a\001
|
|
||||||
|
|
||||||
At this point, all CTCP replies get extracted, giving 1 CTCP reply and
|
|
||||||
no normal NOTICE:
|
|
||||||
|
|
||||||
USERINFO :CS student\n\\atest\\a
|
|
||||||
|
|
||||||
The remaining reply gets CTCP dequoted into:
|
|
||||||
|
|
||||||
USERINFO :CS student\n\001test\001
|
|
||||||
|
|
||||||
and presumly displayed to user actor.
|
|
||||||
|
|
||||||
*******************
|
|
||||||
KNOWN EXTENDED DATA
|
|
||||||
*******************
|
|
||||||
|
|
||||||
Extended data passed between clients can be used to pass structured
|
|
||||||
information between them. Currently known extended data types are:
|
|
||||||
|
|
||||||
ACTION - Used to simulate "role playing" on IRC.
|
|
||||||
DCC - Negotiates file transfers and direct tcp chat
|
|
||||||
connections between clients.
|
|
||||||
SED - Used to send encrypted messages between clients.
|
|
||||||
|
|
||||||
ACTION
|
|
||||||
======
|
|
||||||
This is used by losers on IRC to simulate "role playing" games. An
|
|
||||||
action message looks like the following:
|
|
||||||
|
|
||||||
\001ACTION barfs on the floor.\001
|
|
||||||
|
|
||||||
Clients that recieve such a message should format them to indicate the
|
|
||||||
user who did this is performing an "action". For example, if the user
|
|
||||||
"actor" sent the above message to the channel "#twilight_zone", other
|
|
||||||
users clients might display the message as:
|
|
||||||
|
|
||||||
[ACTION] actor->#twilight_zone: barfs on the floor.
|
|
||||||
|
|
||||||
Presumably other users on the channel are suitably impressed.
|
|
||||||
|
|
||||||
DCC
|
|
||||||
===
|
|
||||||
DCC stands for something like "Direct Client Connection". CTCP DCC
|
|
||||||
extended data messages are used to negotiate file transfers between
|
|
||||||
clients and to negotiate chat connections over tcp connections between
|
|
||||||
two clients, with no IRC server involved. Connections between clients
|
|
||||||
involve protocols other than the usual IRC protocol. Due to this
|
|
||||||
complexity, a full description of the DCC protocol is included
|
|
||||||
separately at the end of this document in Appendix A.
|
|
||||||
|
|
||||||
SED
|
|
||||||
===
|
|
||||||
SED probably stands for something like "Simple Encryption D???". It is
|
|
||||||
used by clients to exchange encrypted messages between clients. A
|
|
||||||
message encoded by SED probably looks something like:
|
|
||||||
|
|
||||||
\001SED encrypted-text-goes-here\001
|
|
||||||
|
|
||||||
Clients which accept such messages should display them in decrypted
|
|
||||||
form. It would be nice if someone documented this, and included the
|
|
||||||
encryption scheme in an Appendix B.
|
|
||||||
|
|
||||||
*************************
|
|
||||||
KNOWN REQUEST/REPLY PAIRS
|
|
||||||
*************************
|
|
||||||
|
|
||||||
A request/reply pair is sent between the two clients in two phases.
|
|
||||||
The first phase is to send the request. This is done with a "privmsg"
|
|
||||||
command (either to a nick or to a channel -- it doesn't matter).
|
|
||||||
|
|
||||||
The second phase is to send a reply. This is done with a "notice"
|
|
||||||
command.
|
|
||||||
|
|
||||||
The known request/reply pairs are for the following commands.
|
|
||||||
|
|
||||||
FINGER - Returns the user's full name, and idle time.
|
|
||||||
VERSION - The version and type of the client.
|
|
||||||
SOURCE - Where to obtain a copy of a client.
|
|
||||||
USERINFO - A string set by the user (never the client coder)
|
|
||||||
CLIENTINFO - Dynamic master index of what a client knows.
|
|
||||||
ERRMSG - Used when an error needs to be replied with.
|
|
||||||
PING - Used to measure the delay of the IRC network
|
|
||||||
between clients.
|
|
||||||
TIME - Gets the local date and time from other clients.
|
|
||||||
|
|
||||||
FINGER
|
|
||||||
======
|
|
||||||
This is used to get a user's real name, and perhaps also the idle time
|
|
||||||
of the user (this usage has been obsoleted by enhancements to the IRC
|
|
||||||
protocol. The request is in a "privmsg" and looks like
|
|
||||||
|
|
||||||
\001FINGER\001
|
|
||||||
|
|
||||||
while the reply is in a "notice" and looks like
|
|
||||||
|
|
||||||
\001FINGER :#\001
|
|
||||||
|
|
||||||
where the # denotes contains information about the users real name,
|
|
||||||
login name at clientmachine and idle time and is of type X-N-AS.
|
|
||||||
|
|
||||||
VERSION
|
|
||||||
=======
|
|
||||||
This is used to get information about the name of the other client and
|
|
||||||
the version of it. The request in a "privmsg" is simply
|
|
||||||
|
|
||||||
\001VERSION\001
|
|
||||||
|
|
||||||
and the reply
|
|
||||||
|
|
||||||
\001VERSION #:#:#\001
|
|
||||||
|
|
||||||
where the first # denotes the name of the client, the second # denotes
|
|
||||||
the version of the client, the third # the enviroment the client is
|
|
||||||
running in.
|
|
||||||
|
|
||||||
Using
|
|
||||||
|
|
||||||
X-N-CLN ::= '\000' .. '\071' | '\073' .. '\377'
|
|
||||||
|
|
||||||
the client name is a string of type X-N-CLN saying things like "Kiwi"
|
|
||||||
or "ircII", the version saying things like "5.2" or "2.1.5c", the
|
|
||||||
enviroment saying things like "GNU Emacs 18.57.19 under SunOS 4.1.1 on
|
|
||||||
Sun SLC" or "Compiled with gcc -ansi under Ultrix 4.0 on VAX-11/730".
|
|
||||||
|
|
||||||
|
|
||||||
SOURCE
|
|
||||||
|
|
||||||
This is used to get information about where to get a copy of the
|
|
||||||
client. The request in a "privmsg" is simply
|
|
||||||
|
|
||||||
\001SOURCE\001
|
|
||||||
|
|
||||||
and the reply is zero or more CTCP replies of the form
|
|
||||||
|
|
||||||
\001SOURCE #:#:#\001
|
|
||||||
|
|
||||||
followed by an end marker
|
|
||||||
|
|
||||||
\001SOURCE\001
|
|
||||||
|
|
||||||
where the first # is the name of an Internet host where the client can
|
|
||||||
be gotten from with anonymous FTP the second # a directory names, and
|
|
||||||
the third # a space separated list of files to be gotten from that
|
|
||||||
directory.
|
|
||||||
|
|
||||||
Using
|
|
||||||
|
|
||||||
X-N-SPC ::= '\000' .. '\037' | '\041' .. '\377'
|
|
||||||
|
|
||||||
the name of the FTP site is to be given by name like "cs.bu.edu" or
|
|
||||||
"funic.funet.fi".
|
|
||||||
|
|
||||||
The file name field is a directory specification optionally followed
|
|
||||||
by one or more file names, delimited by spaces. If only a directory
|
|
||||||
name is given, all files in that directory should be copied when
|
|
||||||
retrieving the clients source. If some files are given, only those
|
|
||||||
files in that directpry should be copied. Note that the spcification
|
|
||||||
allows for all characters but space in the names, this includes
|
|
||||||
allowing :. Examples are "pub/emacs/irc/" to get all files in
|
|
||||||
directory pub/emacs/irc/, the client should be able to first login as
|
|
||||||
user "ftp" and the give the command "CD pub/emacs/irc/", followed by
|
|
||||||
the command "mget *". (It of course has to take care of binary and
|
|
||||||
prompt mode too). Another example is "/pub/irc Kiwi.5.2.el.Z" in which
|
|
||||||
case a "CD /pub/irc" and "get Kiwi.5.2.el.Z" is what should be done.
|
|
||||||
|
|
||||||
|
|
||||||
USERINFO
|
|
||||||
========
|
|
||||||
This is used to transmit a string which is settable by the user (and
|
|
||||||
never should be set by the client). The query is simply
|
|
||||||
|
|
||||||
\001USERINFO\001
|
|
||||||
|
|
||||||
with the reply
|
|
||||||
|
|
||||||
\001USERINFO :#\001
|
|
||||||
|
|
||||||
where the # is the value of the string the client's user has set.
|
|
||||||
|
|
||||||
CLIENTINFO
|
|
||||||
==========
|
|
||||||
This is for client developers use to make it easier to show other
|
|
||||||
client hackers what a certain client knows when it comes to CTCP. The
|
|
||||||
replies should be fairly verbose explaining what CTCP commands are
|
|
||||||
understood, what arguments are expected of what type, and what replies
|
|
||||||
might be expected from the client.
|
|
||||||
|
|
||||||
The query is the word CLIENTINFO in a "privmsg" optionally followed by
|
|
||||||
a colon and one or more specifying words delimited by spaces, where
|
|
||||||
the word CLIENTINFO by itself,
|
|
||||||
|
|
||||||
\001CLIENTINFO\001
|
|
||||||
|
|
||||||
should be replied to by giving a list of known tags (see above in
|
|
||||||
section TAGGED DATA). This is only intended to be read by humans.
|
|
||||||
|
|
||||||
With one argument, the reply should be a description of how to use
|
|
||||||
that tag. With two arguments, a description of how to use that
|
|
||||||
tag's subcommand. And so on.
|
|
||||||
|
|
||||||
ERRMSG
|
|
||||||
======
|
|
||||||
This is used as a reply whenever an unknown query is seen. Also, when
|
|
||||||
used as a query, the reply should echo back the text in the query,
|
|
||||||
together with an indication that no error has happened. Should the
|
|
||||||
query form be used, it is
|
|
||||||
|
|
||||||
\001ERRMSG #\001
|
|
||||||
|
|
||||||
where # is a string containing any character, with the reply
|
|
||||||
|
|
||||||
\001ERRMSG # :#\001
|
|
||||||
|
|
||||||
where the first # is the same string as in the query and the second #
|
|
||||||
a short text notifying the user that no error has occurred.
|
|
||||||
|
|
||||||
A normal ERRMSG reply which is sent when a corrupted query or some
|
|
||||||
corrupted extended data is received, looks like
|
|
||||||
|
|
||||||
\001ERRMSG # :#\001
|
|
||||||
|
|
||||||
where the first # is the the failed query or corrupted extended data
|
|
||||||
and the second # a text explaining what the problem is, like "unknown
|
|
||||||
query" or "failed decrypting text".
|
|
||||||
|
|
||||||
PING
|
|
||||||
====
|
|
||||||
Ping is used to measure the time delay between clients on the IRC
|
|
||||||
network. A ping query is encoded in a privmsg, and has the form:
|
|
||||||
|
|
||||||
\001PING timestamp\001
|
|
||||||
|
|
||||||
where `timestamp' is the current time encoded in any form the querying
|
|
||||||
client finds convienent. The replying client sends back an identical
|
|
||||||
message inside a notice:
|
|
||||||
|
|
||||||
\001PING timestamp\001
|
|
||||||
|
|
||||||
The querying client can then subtract the recieved timestamp from the
|
|
||||||
current time to obtain the delay between clients over the IRC network.
|
|
||||||
|
|
||||||
TIME
|
|
||||||
====
|
|
||||||
Time queries are used to determine what time it is where another
|
|
||||||
user's client is running. This can be useful to determine if someone
|
|
||||||
is probably awake or not, or what timezone they are in. A time query
|
|
||||||
has the form:
|
|
||||||
|
|
||||||
\001TIME\001
|
|
||||||
|
|
||||||
On reciept of such a query in a privmsg, clients should reply with a
|
|
||||||
notice of the form:
|
|
||||||
|
|
||||||
\001TIME :human-readable-time-string\001
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
\001TIME :Thu Aug 11 22:52:51 1994 CST\001
|
|
||||||
|
|
||||||
********
|
|
||||||
EXAMPLES
|
|
||||||
********
|
|
||||||
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001FINGER\001
|
|
||||||
|
|
||||||
might return
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001FINGER :Please check my USERINFO
|
|
||||||
instead :Klaus Zeuge (sojge@mizar) 1 second has passed since
|
|
||||||
victim gave a command last.\001
|
|
||||||
|
|
||||||
(this is only one line) or why not
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001FINGER :Please check my USERINFO
|
|
||||||
instead :Klaus Zeuge (sojge@mizar) 427 seconds (7 minutes and
|
|
||||||
7 seconds) have passed since victim gave a command last.\001
|
|
||||||
|
|
||||||
if Klaus Zeuge happens to be lazy? :-)
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001CLIENTINFO\001
|
|
||||||
|
|
||||||
might return
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001CLIENTINFO :You can request help of the
|
|
||||||
commands CLIENTINFO ERRMSG FINGER USERINFO VERSION by giving
|
|
||||||
an argument to CLIENTINFO.\001
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001CLIENTINFO CLIENTINFO\001
|
|
||||||
|
|
||||||
might return
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001CLIENTINFO :CLIENTINFO with 0
|
|
||||||
arguments gives a list of known client query keywords. With 1
|
|
||||||
argument, a description of the client query keyword is
|
|
||||||
returned.\001
|
|
||||||
|
|
||||||
while sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001clientinfo clientinfo\001
|
|
||||||
|
|
||||||
probably will return something like
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001ERRMSG clientinfo clientinfo :Query is
|
|
||||||
unknown\001
|
|
||||||
|
|
||||||
as tag "clientinfo" isn't known.
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001CLIENTINFO ERRMSG\001
|
|
||||||
|
|
||||||
might return
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001CLIENTINFO :ERRMSG is the given answer
|
|
||||||
on seeing an unknown keyword. When seeing the keyword ERRMSG,
|
|
||||||
it works like an echo.\001
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001USERINFO\001
|
|
||||||
|
|
||||||
might return the somewhat pathetically long
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001USERINFO :I'm studying computer
|
|
||||||
science in Uppsala, I'm male (somehow, that seems to be an
|
|
||||||
important matter on IRC:-) and I speak fluent swedish, decent
|
|
||||||
german, and some english.\001
|
|
||||||
|
|
||||||
Sending
|
|
||||||
|
|
||||||
PRIVMSG victim :\001VERSION\001
|
|
||||||
|
|
||||||
might return:
|
|
||||||
|
|
||||||
:victim NOTICE actor :\001VERSION Kiwi:5.2:GNU Emacs
|
|
||||||
18.57.19 under SunOS 4.1.1 on Sun
|
|
||||||
SLC:FTP.Lysator.LiU.SE:/pub/emacs Kiwi-5.2.el.Z
|
|
||||||
Kiwi.README\001
|
|
||||||
|
|
||||||
if the client is named Kiwi of version 5.2 and is used under GNU Emacs
|
|
||||||
18.57.19 running on a Sun SLCwith SunOS 4.1.1. The client claims a
|
|
||||||
copy of it can be found with anonymous FTP on FTP.Lysator.LiU.SE after
|
|
||||||
giving the FTP command "cd /pub/emacs/". There, one should get files
|
|
||||||
Kiwi-5.2.el.Z and Kiwi.README; presumably one of the files tells how to
|
|
||||||
proceed with building the client after having gotten the files.
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
**********************************************************************
|
|
||||||
Appendix A -- A description of the DCC protocol
|
|
||||||
**********************************************************************
|
|
||||||
|
|
||||||
By Troy Rollo (troy@plod.cbme.unsw.oz.au)
|
|
||||||
Revised by Ben Mesander (ben@gnu.ai.mit.edu)
|
|
||||||
|
|
||||||
Troy Rollo, the original implementor of the DCC protocol, said
|
|
||||||
that the DCC protocol was never designed to be portable to clients
|
|
||||||
other than IRCII. However, time has shown that DCC is useable in
|
|
||||||
environments other than IRCII. IRC clients in diverse languages, such
|
|
||||||
as ksh, elisp, C, and perl have all had DCC implementations.
|
|
||||||
|
|
||||||
Why DCC?
|
|
||||||
========
|
|
||||||
|
|
||||||
DCC allows the user to overcome some limitations of the IRC
|
|
||||||
server network and to have a somewhat more secure chat connection
|
|
||||||
while still in an IRC-oriented protocol.
|
|
||||||
|
|
||||||
DCC uses direct TCP connections between the clients taking
|
|
||||||
part to carry data. There is no flood control, so packets can be sent
|
|
||||||
at full speed, and there is no dependance on server links (or load
|
|
||||||
imposed on them). In addition, since only the initial handshake for
|
|
||||||
DCC conections is passed through the IRC network, it makes it harder
|
|
||||||
for operators with cracked servers to spy on personal messages.
|
|
||||||
|
|
||||||
How?
|
|
||||||
====
|
|
||||||
|
|
||||||
The initial socket for a DCC connection is created
|
|
||||||
by the side that initiates (Offers) the connection. This socket
|
|
||||||
should be a TCP socket bound to INADDR_ANY, listening for
|
|
||||||
connections.
|
|
||||||
|
|
||||||
The Initiating client, on creating the socket, should
|
|
||||||
send its details to the target client using the CTCP command
|
|
||||||
DCC. This command takes the form:
|
|
||||||
|
|
||||||
DCC type argument address port [size]
|
|
||||||
|
|
||||||
type - The connection type.
|
|
||||||
argument - The connectin type dependant argument.
|
|
||||||
address - The host address of the initiator as an integer.
|
|
||||||
port - The port or the socket on which the initiator expects
|
|
||||||
to receive the connection.
|
|
||||||
size - If the connection type is "SEND" (see below), then size
|
|
||||||
will indicate the size of the file being offered. Obsolete
|
|
||||||
IRCII clients do not send this, so be prepared if this is
|
|
||||||
not present.
|
|
||||||
|
|
||||||
The address, port, and size should be sent as ASCII representations of
|
|
||||||
the decimal integer formed by converting the values to host byte order
|
|
||||||
and treating them as an unsigned long, unsigned short, and unsigned
|
|
||||||
long respectively.
|
|
||||||
|
|
||||||
Implementations of the DCC protocol should be prepared to
|
|
||||||
accept further arguments in a CTCP DCC message. There has been some
|
|
||||||
discussion of adding another argument that would specify the type of
|
|
||||||
file being transferred - text, binary, and perhaps others if DCC is
|
|
||||||
implemented on operating systems other than UNIX. If additional
|
|
||||||
arguments are added to the protocol, they should have semantics such
|
|
||||||
that clients which ignore them will interoperate with clients that
|
|
||||||
don't in a sensible way.
|
|
||||||
|
|
||||||
The following DCC connection types are defined:
|
|
||||||
|
|
||||||
Type Purpose Argument
|
|
||||||
CHAT To carry on a semi-secure conversation the string "chat"
|
|
||||||
SEND To send a file to the recipient the file name
|
|
||||||
|
|
||||||
Although the following subcommand is included in the IRCII DCC command,
|
|
||||||
it does _not_ transmit a DCC request via IRC, and thus is not
|
|
||||||
discussed in this document:
|
|
||||||
|
|
||||||
TALK Establishes a TALK connection
|
|
||||||
|
|
||||||
|
|
||||||
Implementation
|
|
||||||
==============
|
|
||||||
|
|
||||||
The CHAT and SEND connection types should not be
|
|
||||||
accepted automatically as this would create the potential for
|
|
||||||
terrorism. Instead, they should notify the user that an
|
|
||||||
offer has been made, and allow the user to accept it.
|
|
||||||
|
|
||||||
The recipient should have the opportunity to rename a file
|
|
||||||
offered with the DCC SEND command prior to retrieving it. It is also
|
|
||||||
desirable to ensure that the offered file will not overwrite an
|
|
||||||
existing file.
|
|
||||||
|
|
||||||
Older IRCII clients send the entire pathname of the file being
|
|
||||||
transmitted. This is annoying, and newer clients should simply send
|
|
||||||
the filename portion of the file being transmitted.
|
|
||||||
|
|
||||||
The port number should be scrutinized - if the port number is
|
|
||||||
in the UNIX reserved port range, the connection should only be
|
|
||||||
accepted with caution.
|
|
||||||
|
|
||||||
If it is not possible in the client implementation language to
|
|
||||||
handle a 32-bit integer (for instance emacs 18 elisp and ksh 88), then
|
|
||||||
it is often possible to use the hostname in the originating PRIVMSG.
|
|
||||||
|
|
||||||
The following are the steps which should occur in the clients
|
|
||||||
(this description assumes use of the BSD socket interface on a UNIX
|
|
||||||
system).
|
|
||||||
|
|
||||||
Initiator:
|
|
||||||
DCC command issued.
|
|
||||||
Create a socket, bind it to INADDR_ANY, port 0, and
|
|
||||||
make it passive (a listening socket).
|
|
||||||
Send the recipient a DCC request via CTCP supplying
|
|
||||||
the address and port of the socket. (This
|
|
||||||
is ideally taken from the address of the local
|
|
||||||
side of the socket which is connected to a
|
|
||||||
server. This is presumably the interface on
|
|
||||||
the host which is closest to the rest of
|
|
||||||
the net, and results in one less routing hop
|
|
||||||
in the case of gateway nodes).
|
|
||||||
Continue normally until a connection is received.
|
|
||||||
|
|
||||||
On a connection:
|
|
||||||
Accept the connection.
|
|
||||||
Close the original passive socket.
|
|
||||||
Conduct transaction on the new socket.
|
|
||||||
|
|
||||||
Acceptor:
|
|
||||||
CTCP DCC request received.
|
|
||||||
Record information on the DCC request and notify the user.
|
|
||||||
|
|
||||||
At this point, the USER should be able to abort (close) the
|
|
||||||
request, or accept it. The request should be accepted with
|
|
||||||
a command specifying the sender, type, and argument, or
|
|
||||||
a subset of these where no ambiguity exists.
|
|
||||||
|
|
||||||
If accepted, create a TCP socket.
|
|
||||||
Connect the new socket to the address and port supplied.
|
|
||||||
Conduct the transaction over the socket.
|
|
||||||
|
|
||||||
|
|
||||||
Type specific details.
|
|
||||||
======================
|
|
||||||
|
|
||||||
CHAT Data sent across a CHAT connection should be sent line-by-line
|
|
||||||
without any prefixes or commands. A CHAT connection ends when
|
|
||||||
one party issues the DCC CLOSE command to their clients, which
|
|
||||||
causes the socket to be closed and the information on the connection
|
|
||||||
to be discarded. The terminating character of each line is a
|
|
||||||
newline character, '\n'.
|
|
||||||
|
|
||||||
FILE Data is sent in packets, rather than dumped in a stream manner.
|
|
||||||
This allows the DCC SEND connection to survive where an FTP
|
|
||||||
connection might fail. The size of the packets is up to the
|
|
||||||
client, and may be set by the user. Smaller packets result
|
|
||||||
in a higher probability of survival over bad links.
|
|
||||||
The recipient should acknowledge each packet by transmitting
|
|
||||||
the total number of bytes received as an unsigned, 4 byte
|
|
||||||
integer in network byte order. The sender should not continue
|
|
||||||
to transmit until the recipient has acknowledged all data
|
|
||||||
already transmitted. Additionally, the sender should not
|
|
||||||
close the connection until the last byte has been
|
|
||||||
acknowledged by the recipient.
|
|
||||||
|
|
||||||
Older IRCII clients do not send the file size of the file
|
|
||||||
being transmitted via DCC. For those clients, note that it is
|
|
||||||
not possible for the recipient to tell if the entire file has
|
|
||||||
been received - only the sender has that information, although
|
|
||||||
IRCII does not report it. Users generally verify the transfer
|
|
||||||
by checking file sizes. Authors of clients are urged to use
|
|
||||||
the size feature.
|
|
||||||
|
|
||||||
Note also that no provision is made for text translation.
|
|
||||||
|
|
||||||
The original block size used by IRCII was 1024. Other clients
|
|
||||||
have adopted this. Note, however, that an implementation should accept
|
|
||||||
any blocksize. IRCII currently allows a user-settable blocksize.
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,455 +0,0 @@
|
||||||
RFC 2810 (RFC2810)
|
|
||||||
|
|
||||||
Internet RFC/STD/FYI/BCP Archives
|
|
||||||
|
|
||||||
[ RFC Index | RFC Search | Usenet FAQs | Web FAQs | Documents | Cities ]
|
|
||||||
|
|
||||||
Alternate Formats: rfc2810.txt | rfc2810.txt.pdf
|
|
||||||
|
|
||||||
RFC 2810 - Internet Relay Chat: Architecture
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Network Working Group C. Kalt
|
|
||||||
Request for Comments: 2810 April 2000
|
|
||||||
Updates: 1459
|
|
||||||
Category: Informational
|
|
||||||
|
|
||||||
Internet Relay Chat: Architecture
|
|
||||||
|
|
||||||
Status of this Memo
|
|
||||||
|
|
||||||
This memo provides information for the Internet community. It does
|
|
||||||
not specify an Internet standard of any kind. Distribution of this
|
|
||||||
memo is unlimited.
|
|
||||||
|
|
||||||
Copyright Notice
|
|
||||||
|
|
||||||
Copyright (C) The Internet Society (2000). All Rights Reserved.
|
|
||||||
|
|
||||||
Abstract
|
|
||||||
|
|
||||||
The IRC (Internet Relay Chat) protocol is for use with text based
|
|
||||||
conferencing. It has been developed since 1989 when it was originally
|
|
||||||
implemented as a mean for users on a BBS to chat amongst themselves.
|
|
||||||
|
|
||||||
First formally documented in May 1993 by RFC 1459 [IRC], the protocol
|
|
||||||
has kept evolving. This document is an update describing the
|
|
||||||
architecture of the current IRC protocol and the role of its
|
|
||||||
different components. Other documents describe in detail the
|
|
||||||
protocol used between the various components defined here.
|
|
||||||
|
|
||||||
Table of Contents
|
|
||||||
|
|
||||||
1. Introduction ............................................... 2
|
|
||||||
2. Components ................................................. 2
|
|
||||||
2.1 Servers ................................................ 2
|
|
||||||
2.2 Clients ................................................ 3
|
|
||||||
2.2.1 User Clients ...................................... 3
|
|
||||||
2.2.2 Service Clients ................................... 3
|
|
||||||
3. Architecture ............................................... 3
|
|
||||||
4. IRC Protocol Services ...................................... 4
|
|
||||||
4.1 Client Locator ......................................... 4
|
|
||||||
4.2 Message Relaying ....................................... 4
|
|
||||||
4.3 Channel Hosting And Management ......................... 4
|
|
||||||
5. IRC Concepts ............................................... 4
|
|
||||||
5.1 One-To-One Communication ............................... 5
|
|
||||||
5.2 One-To-Many ............................................ 5
|
|
||||||
5.2.1 To A Channel ...................................... 5
|
|
||||||
5.2.2 To A Host/Server Mask ............................. 6
|
|
||||||
|
|
||||||
5.2.3 To A List ......................................... 6
|
|
||||||
5.3 One-To-All ............................................. 6
|
|
||||||
5.3.1 Client-to-Client .................................. 6
|
|
||||||
5.3.2 Client-to-Server .................................. 7
|
|
||||||
5.3.3 Server-to-Server .................................. 7
|
|
||||||
6. Current Problems ........................................... 7
|
|
||||||
6.1 Scalability ............................................ 7
|
|
||||||
6.2 Reliability ............................................ 7
|
|
||||||
6.3 Network Congestion ..................................... 7
|
|
||||||
6.4 Privacy ................................................ 8
|
|
||||||
7. Security Considerations .................................... 8
|
|
||||||
8. Current Support And Availability ........................... 8
|
|
||||||
9. Acknowledgements ........................................... 8
|
|
||||||
10. References ................................................ 8
|
|
||||||
11. Author's Address .......................................... 9
|
|
||||||
12. Full Copyright Statement .................................. 10
|
|
||||||
|
|
||||||
1. Introduction
|
|
||||||
|
|
||||||
The IRC (Internet Relay Chat) protocol has been designed over a
|
|
||||||
number of years for use with text based conferencing. This document
|
|
||||||
describes its current architecture.
|
|
||||||
|
|
||||||
The IRC Protocol is based on the client-server model, and is well
|
|
||||||
suited to running on many machines in a distributed fashion. A
|
|
||||||
typical setup involves a single process (the server) forming a
|
|
||||||
central point for clients (or other servers) to connect to,
|
|
||||||
performing the required message delivery/multiplexing and other
|
|
||||||
functions.
|
|
||||||
|
|
||||||
This distributed model, which requires each server to have a copy
|
|
||||||
of the global state information, is still the most flagrant problem
|
|
||||||
of the protocol as it is a serious handicap, which limits the maximum
|
|
||||||
size a network can reach. If the existing networks have been able to
|
|
||||||
keep growing at an incredible pace, we must thank hardware
|
|
||||||
manufacturers for giving us ever more powerful systems.
|
|
||||||
|
|
||||||
2. Components
|
|
||||||
|
|
||||||
The following paragraphs define the basic components of the IRC
|
|
||||||
protocol.
|
|
||||||
|
|
||||||
2.1 Servers
|
|
||||||
|
|
||||||
The server forms the backbone of IRC as it is the only component
|
|
||||||
of the protocol which is able to link all the other components
|
|
||||||
together: it provides a point to which clients may connect to talk to
|
|
||||||
|
|
||||||
each other [IRC-CLIENT], and a point for other servers to connect to
|
|
||||||
[IRC-SERVER]. The server is also responsible for providing the basic
|
|
||||||
services defined by the IRC protocol.
|
|
||||||
|
|
||||||
2.2 Clients
|
|
||||||
|
|
||||||
A client is anything connecting to a server that is not another
|
|
||||||
server. There are two types of clients which both serve a different
|
|
||||||
purpose.
|
|
||||||
|
|
||||||
2.2.1 User Clients
|
|
||||||
|
|
||||||
User clients are generally programs providing a text based
|
|
||||||
interface that is used to communicate interactively via IRC. This
|
|
||||||
particular type of clients is often referred as "users".
|
|
||||||
|
|
||||||
2.2.2 Service Clients
|
|
||||||
|
|
||||||
Unlike users, service clients are not intended to be used manually
|
|
||||||
nor for talking. They have a more limited access to the chat
|
|
||||||
functions of the protocol, while optionally having access to more
|
|
||||||
private data from the servers.
|
|
||||||
|
|
||||||
Services are typically automatons used to provide some kind of
|
|
||||||
service (not necessarily related to IRC itself) to users. An example
|
|
||||||
is a service collecting statistics about the origin of users
|
|
||||||
connected on the IRC network.
|
|
||||||
|
|
||||||
3. Architecture
|
|
||||||
|
|
||||||
An IRC network is defined by a group of servers connected to each
|
|
||||||
other. A single server forms the simplest IRC network.
|
|
||||||
|
|
||||||
The only network configuration allowed for IRC servers is that of
|
|
||||||
a spanning tree where each server acts as a central node for the rest
|
|
||||||
of the network it sees.
|
|
||||||
|
|
||||||
1--\
|
|
||||||
A D---4
|
|
||||||
2--/ \ /
|
|
||||||
B----C
|
|
||||||
/ \
|
|
||||||
3 E
|
|
||||||
|
|
||||||
Servers: A, B, C, D, E Clients: 1, 2, 3, 4
|
|
||||||
|
|
||||||
[ Fig. 1. Sample small IRC network ]
|
|
||||||
|
|
||||||
The IRC protocol provides no mean for two clients to directly
|
|
||||||
communicate. All communication between clients is relayed by the
|
|
||||||
server(s).
|
|
||||||
|
|
||||||
4. IRC Protocol Services
|
|
||||||
|
|
||||||
This section describes the services offered by the IRC protocol. The
|
|
||||||
combination of these services allow real-time conferencing.
|
|
||||||
|
|
||||||
4.1 Client Locator
|
|
||||||
|
|
||||||
To be able to exchange messages, two clients must be able to locate
|
|
||||||
each other.
|
|
||||||
|
|
||||||
Upon connecting to a server, a client registers using a label which
|
|
||||||
is then used by other servers and clients to know where the client is
|
|
||||||
located. Servers are responsible for keeping track of all the labels
|
|
||||||
being used.
|
|
||||||
|
|
||||||
4.2 Message Relaying
|
|
||||||
|
|
||||||
The IRC protocol provides no mean for two clients to directly
|
|
||||||
communicate. All communication between clients is relayed by the
|
|
||||||
server(s).
|
|
||||||
|
|
||||||
4.3 Channel Hosting And Management
|
|
||||||
|
|
||||||
A channel is a named group of one or more users which will all
|
|
||||||
receive messages addressed to that channel. A channel is
|
|
||||||
characterized by its name and current members, it also has a set of
|
|
||||||
properties which can be manipulated by (some of) its members.
|
|
||||||
|
|
||||||
Channels provide a mean for a message to be sent to several clients.
|
|
||||||
Servers host channels, providing the necessary message multiplexing.
|
|
||||||
Servers are also responsible for managing channels by keeping track
|
|
||||||
of the channel members. The exact role of servers is defined in
|
|
||||||
"Internet Relay Chat: Channel Management" [IRC-CHAN].
|
|
||||||
|
|
||||||
5. IRC Concepts
|
|
||||||
|
|
||||||
This section is devoted to describing the actual concepts behind the
|
|
||||||
organization of the IRC protocol and how different classes of
|
|
||||||
messages are delivered.
|
|
||||||
|
|
||||||
5.1 One-To-One Communication
|
|
||||||
|
|
||||||
Communication on a one-to-one basis is usually performed by clients,
|
|
||||||
since most server-server traffic is not a result of servers talking
|
|
||||||
only to each other. To provide a means for clients to talk to each
|
|
||||||
other, it is REQUIRED that all servers be able to send a message in
|
|
||||||
exactly one direction along the spanning tree in order to reach any
|
|
||||||
client. Thus the path of a message being delivered is the shortest
|
|
||||||
path between any two points on the spanning tree.
|
|
||||||
|
|
||||||
The following examples all refer to Figure 1 above.
|
|
||||||
|
|
||||||
Example 1: A message between clients 1 and 2 is only seen by server
|
|
||||||
A, which sends it straight to client 2.
|
|
||||||
|
|
||||||
Example 2: A message between clients 1 and 3 is seen by servers A &
|
|
||||||
B, and client 3. No other clients or servers are allowed see the
|
|
||||||
message.
|
|
||||||
|
|
||||||
Example 3: A message between clients 2 and 4 is seen by servers A, B,
|
|
||||||
C & D and client 4 only.
|
|
||||||
|
|
||||||
5.2 One-To-Many
|
|
||||||
|
|
||||||
The main goal of IRC is to provide a forum which allows easy and
|
|
||||||
efficient conferencing (one to many conversations). IRC offers
|
|
||||||
several means to achieve this, each serving its own purpose.
|
|
||||||
|
|
||||||
5.2.1 To A Channel
|
|
||||||
|
|
||||||
In IRC the channel has a role equivalent to that of the multicast
|
|
||||||
group; their existence is dynamic and the actual conversation carried
|
|
||||||
out on a channel MUST only be sent to servers which are supporting
|
|
||||||
users on a given channel. Moreover, the message SHALL only be sent
|
|
||||||
once to every local link as each server is responsible to fan the
|
|
||||||
original message to ensure that it will reach all the recipients.
|
|
||||||
|
|
||||||
The following examples all refer to Figure 2.
|
|
||||||
|
|
||||||
Example 4: Any channel with 1 client in it. Messages to the channel
|
|
||||||
go to the server and then nowhere else.
|
|
||||||
|
|
||||||
Example 5: 2 clients in a channel. All messages traverse a path as if
|
|
||||||
they were private messages between the two clients outside a
|
|
||||||
channel.
|
|
||||||
|
|
||||||
Example 6: Clients 1, 2 and 3 in a channel. All messages to the
|
|
||||||
channel are sent to all clients and only those servers which must
|
|
||||||
be traversed by the message if it were a private message to a
|
|
||||||
single client. If client 1 sends a message, it goes back to
|
|
||||||
client 2 and then via server B to client 3.
|
|
||||||
|
|
||||||
5.2.2 To A Host/Server Mask
|
|
||||||
|
|
||||||
To provide with some mechanism to send messages to a large body of
|
|
||||||
related users, host and server mask messages are available. These
|
|
||||||
messages are sent to users whose host or server information match
|
|
||||||
that of the mask. The messages are only sent to locations where
|
|
||||||
users are, in a fashion similar to that of channels.
|
|
||||||
|
|
||||||
5.2.3 To A List
|
|
||||||
|
|
||||||
The least efficient style of one-to-many conversation is through
|
|
||||||
clients talking to a 'list' of targets (client, channel, mask). How
|
|
||||||
this is done is almost self explanatory: the client gives a list of
|
|
||||||
destinations to which the message is to be delivered and the server
|
|
||||||
breaks it up and dispatches a separate copy of the message to each
|
|
||||||
given destination.
|
|
||||||
|
|
||||||
This is not as efficient as using a channel since the destination
|
|
||||||
list MAY be broken up and the dispatch sent without checking to make
|
|
||||||
sure duplicates aren't sent down each path.
|
|
||||||
|
|
||||||
5.3 One-To-All
|
|
||||||
|
|
||||||
The one-to-all type of message is better described as a broadcast
|
|
||||||
message, sent to all clients or servers or both. On a large network
|
|
||||||
of users and servers, a single message can result in a lot of traffic
|
|
||||||
being sent over the network in an effort to reach all of the desired
|
|
||||||
destinations.
|
|
||||||
|
|
||||||
For some class of messages, there is no option but to broadcast it to
|
|
||||||
all servers so that the state information held by each server is
|
|
||||||
consistent between servers.
|
|
||||||
|
|
||||||
5.3.1 Client-to-Client
|
|
||||||
|
|
||||||
There is no class of message which, from a single message, results in
|
|
||||||
a message being sent to every other client.
|
|
||||||
|
|
||||||
5.3.2 Client-to-Server
|
|
||||||
|
|
||||||
Most of the commands which result in a change of state information
|
|
||||||
(such as channel membership, channel mode, user status, etc.) MUST be
|
|
||||||
sent to all servers by default, and this distribution SHALL NOT be
|
|
||||||
changed by the client.
|
|
||||||
|
|
||||||
5.3.3 Server-to-Server
|
|
||||||
|
|
||||||
While most messages between servers are distributed to all 'other'
|
|
||||||
servers, this is only required for any message that affects a user,
|
|
||||||
channel or server. Since these are the basic items found in IRC,
|
|
||||||
nearly all messages originating from a server are broadcast to all
|
|
||||||
other connected servers.
|
|
||||||
|
|
||||||
6. Current Problems
|
|
||||||
|
|
||||||
There are a number of recognized problems with this protocol, this
|
|
||||||
section only addresses the problems related to the architecture of
|
|
||||||
the protocol.
|
|
||||||
|
|
||||||
6.1 Scalability
|
|
||||||
|
|
||||||
It is widely recognized that this protocol does not scale
|
|
||||||
sufficiently well when used in a large arena. The main problem comes
|
|
||||||
from the requirement that all servers know about all other servers,
|
|
||||||
clients and channels and that information regarding them be updated
|
|
||||||
as soon as it changes.
|
|
||||||
|
|
||||||
6.2 Reliability
|
|
||||||
|
|
||||||
As the only network configuration allowed for IRC servers is that of
|
|
||||||
a spanning tree, each link between two servers is an obvious and
|
|
||||||
quite serious point of failure. This particular issue is addressed
|
|
||||||
more in detail in "Internet Relay Chat: Server Protocol" [IRC-
|
|
||||||
SERVER].
|
|
||||||
|
|
||||||
6.3 Network Congestion
|
|
||||||
|
|
||||||
Another problem related to the scalability and reliability issues, as
|
|
||||||
well as the spanning tree architecture, is that the protocol and
|
|
||||||
architecture for IRC are extremely vulnerable to network congestions.
|
|
||||||
This problem is endemic, and should be solved for the next
|
|
||||||
generation: if congestion and high traffic volume cause a link
|
|
||||||
between two servers to fail, not only this failure generates more
|
|
||||||
network traffic, but the reconnection (eventually elsewhere) of two
|
|
||||||
servers also generates more traffic.
|
|
||||||
|
|
||||||
In an attempt to minimize the impact of these problems, it is
|
|
||||||
strongly RECOMMENDED that servers do not automatically try to
|
|
||||||
reconnect too fast, in order to avoid aggravating the situation.
|
|
||||||
|
|
||||||
6.4 Privacy
|
|
||||||
|
|
||||||
Besides not scaling well, the fact that servers need to know all
|
|
||||||
information about other entities, the issue of privacy is also a
|
|
||||||
concern. This is in particular true for channels, as the related
|
|
||||||
information is quite a lot more revealing than whether a user is
|
|
||||||
online or not.
|
|
||||||
|
|
||||||
7. Security Considerations
|
|
||||||
|
|
||||||
Asides from the privacy concerns mentioned in section 6.4 (Privacy),
|
|
||||||
security is believed to be irrelevant to this document.
|
|
||||||
|
|
||||||
8. Current Support And Availability
|
|
||||||
|
|
||||||
Mailing lists for IRC related discussion:
|
|
||||||
General discussion: ircd-users@irc.org
|
|
||||||
Protocol development: ircd-dev@irc.org
|
|
||||||
|
|
||||||
Software implementations:
|
|
||||||
ftp://ftp.irc.org/irc/server
|
|
||||||
ftp://ftp.funet.fi/pub/unix/irc
|
|
||||||
ftp://coombs.anu.edu.au/pub/irc
|
|
||||||
|
|
||||||
Newsgroup: alt.irc
|
|
||||||
|
|
||||||
9. Acknowledgements
|
|
||||||
|
|
||||||
Parts of this document were copied from the RFC 1459 [IRC] which
|
|
||||||
first formally documented the IRC Protocol. It has also benefited
|
|
||||||
from many rounds of review and comments. In particular, the
|
|
||||||
following people have made significant contributions to this
|
|
||||||
document:
|
|
||||||
|
|
||||||
Matthew Green, Michael Neumayer, Volker Paulsen, Kurt Roeckx, Vesa
|
|
||||||
Ruokonen, Magnus Tjernstrom, Stefan Zehl.
|
|
||||||
|
|
||||||
10. References
|
|
||||||
|
|
||||||
[KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
|
|
||||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
|
||||||
|
|
||||||
[IRC] Oikarinen, J. and D. Reed, "Internet Relay Chat
|
|
||||||
Protocol", RFC 1459, May 1993.
|
|
||||||
|
|
||||||
[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
|
|
||||||
2812, April 2000.
|
|
||||||
|
|
||||||
[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
|
|
||||||
2813, April 2000.
|
|
||||||
|
|
||||||
[IRC-CHAN] Kalt, C., "Internet Relay Chat: Channel Management", RFC
|
|
||||||
2811, April 2000.
|
|
||||||
|
|
||||||
11. Author's Address
|
|
||||||
|
|
||||||
Christophe Kalt
|
|
||||||
99 Teaneck Rd, Apt #117
|
|
||||||
Ridgefield Park, NJ 07660
|
|
||||||
USA
|
|
||||||
|
|
||||||
EMail: kalt@stealth.net
|
|
||||||
|
|
||||||
12. Full Copyright Statement
|
|
||||||
|
|
||||||
Copyright (C) The Internet Society (2000). All Rights Reserved.
|
|
||||||
|
|
||||||
This document and translations of it may be copied and furnished to
|
|
||||||
others, and derivative works that comment on or otherwise explain it
|
|
||||||
or assist in its implementation may be prepared, copied, published
|
|
||||||
and distributed, in whole or in part, without restriction of any
|
|
||||||
kind, provided that the above copyright notice and this paragraph are
|
|
||||||
included on all such copies and derivative works. However, this
|
|
||||||
document itself may not be modified in any way, such as by removing
|
|
||||||
the copyright notice or references to the Internet Society or other
|
|
||||||
Internet organizations, except as needed for the purpose of
|
|
||||||
developing Internet standards in which case the procedures for
|
|
||||||
copyrights defined in the Internet Standards process must be
|
|
||||||
followed, or as required to translate it into languages other than
|
|
||||||
English.
|
|
||||||
|
|
||||||
The limited permissions granted above are perpetual and will not be
|
|
||||||
revoked by the Internet Society or its successors or assigns.
|
|
||||||
|
|
||||||
This document and the information contained herein is provided on an
|
|
||||||
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
|
|
||||||
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
|
|
||||||
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
Acknowledgement
|
|
||||||
|
|
||||||
Funding for the RFC Editor function is currently provided by the
|
|
||||||
Internet Society.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Comments about this RFC:
|
|
||||||
|
|
||||||
* RFC 2810: Hi, Here goes the text in section 5.2.1 : "The following
|
|
||||||
examples all... by AkA (6/2/2007)
|
|
||||||
|
|
||||||
Previous: RFC 2809 - Implementation of Next: RFC 2811 - Internet Relay Chat:
|
|
||||||
L2TP Compulsory Tunneling via RADIUS Channel Management
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
[ RFC Index | RFC Search | Usenet FAQs | Web FAQs | Documents | Cities ]
|
|
|
@ -1,866 +0,0 @@
|
||||||
RFC 2811 (RFC2811)
|
|
||||||
|
|
||||||
Internet RFC/STD/FYI/BCP Archives
|
|
||||||
|
|
||||||
[ RFC Index | RFC Search | Usenet FAQs | Web FAQs | Documents | Cities ]
|
|
||||||
|
|
||||||
Alternate Formats: rfc2811.txt | rfc2811.txt.pdf
|
|
||||||
|
|
||||||
RFC 2811 - Internet Relay Chat: Channel Management
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
Network Working Group C. Kalt
|
|
||||||
Request for Comments: 2811 April 2000
|
|
||||||
Updates: 1459
|
|
||||||
Category: Informational
|
|
||||||
|
|
||||||
Internet Relay Chat: Channel Management
|
|
||||||
|
|
||||||
Status of this Memo
|
|
||||||
|
|
||||||
This memo provides information for the Internet community. It does
|
|
||||||
not specify an Internet standard of any kind. Distribution of this
|
|
||||||
memo is unlimited.
|
|
||||||
|
|
||||||
Copyright Notice
|
|
||||||
|
|
||||||
Copyright (C) The Internet Society (2000). All Rights Reserved.
|
|
||||||
|
|
||||||
Abstract
|
|
||||||
|
|
||||||
One of the most notable characteristics of the IRC (Internet Relay
|
|
||||||
Chat) protocol is to allow for users to be grouped in forums, called
|
|
||||||
channels, providing a mean for multiple users to communicate
|
|
||||||
together.
|
|
||||||
|
|
||||||
There was originally a unique type of channels, but with the years,
|
|
||||||
new types appeared either as a response to a need, or for
|
|
||||||
experimental purposes.
|
|
||||||
|
|
||||||
This document specifies how channels, their characteristics and
|
|
||||||
properties are managed by IRC servers.
|
|
||||||
|
|
||||||
Table of Contents
|
|
||||||
|
|
||||||
1. Introduction ............................................... 2
|
|
||||||
2. Channel Characteristics .................................... 3
|
|
||||||
2.1 Namespace .............................................. 3
|
|
||||||
2.2 Channel Scope .......................................... 3
|
|
||||||
2.3 Channel Properties ..................................... 4
|
|
||||||
2.4 Privileged Channel Members ............................. 4
|
|
||||||
2.4.1 Channel Operators ................................. 5
|
|
||||||
2.4.2 Channel Creator ................................... 5
|
|
||||||
3. Channel lifetime ........................................... 5
|
|
||||||
3.1 Standard channels ...................................... 5
|
|
||||||
3.2 Safe Channels .......................................... 6
|
|
||||||
4. Channel Modes .............................................. 7
|
|
||||||
4.1 Member Status .......................................... 7
|
|
||||||
4.1.1 "Channel Creator" Status .......................... 7
|
|
||||||
|
|
||||||
4.1.2 Channel Operator Status ........................... 8
|
|
||||||
4.1.3 Voice Privilege ................................... 8
|
|
||||||
4.2 Channel Flags .......................................... 8
|
|
||||||
4.2.1 Anonymous Flag .................................... 8
|
|
||||||
4.2.2 Invite Only Flag .................................. 8
|
|
||||||
4.2.3 Moderated Channel Flag ............................ 9
|
|
||||||
4.2.4 No Messages To Channel From Clients On The Outside 9
|
|
||||||
4.2.5 Quiet Channel ..................................... 9
|
|
||||||
4.2.6 Private and Secret Channels ....................... 9
|
|
||||||
4.2.7 Server Reop Flag .................................. 10
|
|
||||||
4.2.8 Topic ............................................. 10
|
|
||||||
4.2.9 User Limit ........................................ 10
|
|
||||||
4.2.10 Channel Key ...................................... 10
|
|
||||||
4.3 Channel Access Control ................................. 10
|
|
||||||
4.3.1 Channel Ban and Exception ......................... 11
|
|
||||||
4.3.2 Channel Invitation ................................ 11
|
|
||||||
5. Current Implementations .................................... 11
|
|
||||||
5.1 Tracking Recently Used Channels ........................ 11
|
|
||||||
5.2 Safe Channels .......................................... 12
|
|
||||||
5.2.1 Channel Identifier ................................ 12
|
|
||||||
5.2.2 Channel Delay ..................................... 12
|
|
||||||
5.2.3 Abuse Window ...................................... 13
|
|
||||||
5.2.4 Preserving Sanity In The Name Space ............... 13
|
|
||||||
5.2.5 Server Reop Mechanism ............................. 13
|
|
||||||
6. Current problems ........................................... 14
|
|
||||||
6.1 Labels ................................................. 14
|
|
||||||
6.1.1 Channel Delay ..................................... 14
|
|
||||||
6.1.2 Safe Channels ..................................... 15
|
|
||||||
6.2 Mode Propagation Delays ................................ 15
|
|
||||||
6.3 Collisions And Channel Modes ........................... 15
|
|
||||||
6.4 Resource Exhaustion .................................... 16
|
|
||||||
7. Security Considerations .................................... 16
|
|
||||||
7.1 Access Control ......................................... 16
|
|
||||||
7.2 Channel Privacy ........................................ 16
|
|
||||||
7.3 Anonymity ............................................... 17
|
|
||||||
8. Current support and availability ........................... 17
|
|
||||||
9. Acknowledgements ........................................... 17
|
|
||||||
10. References ................................................ 18
|
|
||||||
11. Author's Address .......................................... 18
|
|
||||||
12. Full Copyright Statement ................................... 19
|
|
||||||
|
|
||||||
1. Introduction
|
|
||||||
|
|
||||||
This document defines in detail on how channels are managed by the
|
|
||||||
IRC servers and will be mostly useful to people working on
|
|
||||||
implementing an IRC server.
|
|
||||||
|
|
||||||
While the concepts defined here are an important part of IRC, they
|
|
||||||
remain non essential for implementing clients. While the trend seems
|
|
||||||
to be towards more and more complex and "intelligent" clients which
|
|
||||||
are able to take advantage of knowing the internal workings of
|
|
||||||
channels to provide the users with a more friendly interface, simple
|
|
||||||
clients can be implemented without reading this document.
|
|
||||||
|
|
||||||
Many of the concepts defined here were designed with the IRC
|
|
||||||
architecture [IRC-ARCH] in mind and mostly make sense in this
|
|
||||||
context. However, many others could be applied to other
|
|
||||||
architectures in order to provide forums for a conferencing system.
|
|
||||||
|
|
||||||
Finally, it is to be noted that IRC users may find some of the
|
|
||||||
following sections of interest, in particular sections 2 (Channel
|
|
||||||
Characteristics) and 4 (Channel Modes).
|
|
||||||
|
|
||||||
2. Channel Characteristics
|
|
||||||
|
|
||||||
A channel is a named group of one or more users which will all
|
|
||||||
receive messages addressed to that channel. A channel is
|
|
||||||
characterized by its name, properties and current members.
|
|
||||||
|
|
||||||
2.1 Namespace
|
|
||||||
|
|
||||||
Channels names are strings (beginning with a '&', '#', '+' or '!'
|
|
||||||
character) of length up to fifty (50) characters. Channel names are
|
|
||||||
case insensitive.
|
|
||||||
|
|
||||||
Apart from the the requirement that the first character being either
|
|
||||||
'&', '#', '+' or '!' (hereafter called "channel prefix"). The only
|
|
||||||
restriction on a channel name is that it SHALL NOT contain any spaces
|
|
||||||
(' '), a control G (^G or ASCII 7), a comma (',' which is used as a
|
|
||||||
list item separator by the protocol). Also, a colon (':') is used as
|
|
||||||
a delimiter for the channel mask. The exact syntax of a channel name
|
|
||||||
is defined in "IRC Server Protocol" [IRC-SERVER].
|
|
||||||
|
|
||||||
The use of different prefixes effectively creates four (4) distinct
|
|
||||||
namespaces for channel names. This is important because of the
|
|
||||||
protocol limitations regarding namespaces (in general). See section
|
|
||||||
6.1 (Labels) for more details on these limitations.
|
|
||||||
|
|
||||||
2.2 Channel Scope
|
|
||||||
|
|
||||||
A channel entity is known by one or more servers on the IRC network.
|
|
||||||
A user can only become member of a channel known by the server to
|
|
||||||
which the user is directly connected. The list of servers which know
|
|
||||||
|
|
||||||
of the existence of a particular channel MUST be a contiguous part of
|
|
||||||
the IRC network, in order for the messages addressed to the channel
|
|
||||||
to be sent to all the channel members.
|
|
||||||
|
|
||||||
Channels with '&' as prefix are local to the server where they are
|
|
||||||
created.
|
|
||||||
|
|
||||||
Other channels are known to one (1) or more servers that are
|
|
||||||
connected to the network, depending on the channel mask:
|
|
||||||
|
|
||||||
If there is no channel mask, then the channel is known to all
|
|
||||||
the servers.
|
|
||||||
|
|
||||||
If there is a channel mask, then the channel MUST only be known
|
|
||||||
to servers which has a local user on the channel, and to its
|
|
||||||
neighbours if the mask matches both the local and neighbouring
|
|
||||||
server names. Since other servers have absolutely no knowledge of
|
|
||||||
the existence of such a channel, the area formed by the servers
|
|
||||||
having a name matching the mask has to be contiguous for the
|
|
||||||
channel to be known by all these servers. Channel masks are best
|
|
||||||
used in conjunction with server hostmasking [IRC-SERVER].
|
|
||||||
|
|
||||||
2.3 Channel Properties
|
|
||||||
|
|
||||||
Each channel has its own properties, which are defined by channel
|
|
||||||
modes. Channel modes can be manipulated by the channel members. The
|
|
||||||
modes affect the way servers manage the channels.
|
|
||||||
|
|
||||||
Channels with '+' as prefix do not support channel modes. This means
|
|
||||||
that all the modes are unset, with the exception of the 't' channel
|
|
||||||
flag which is set.
|
|
||||||
|
|
||||||
2.4 Privileged Channel Members
|
|
||||||
|
|
||||||
In order for the channel members to keep some control over a channel,
|
|
||||||
and some kind of sanity, some channel members are privileged. Only
|
|
||||||
these members are allowed to perform the following actions on the
|
|
||||||
channel:
|
|
||||||
|
|
||||||
INVITE - Invite a client to an invite-only channel (mode +i)
|
|
||||||
KICK - Eject a client from the channel
|
|
||||||
MODE - Change the channel's mode, as well as
|
|
||||||
members' privileges
|
|
||||||
PRIVMSG - Sending messages to the channel (mode +n, +m, +v)
|
|
||||||
TOPIC - Change the channel topic in a mode +t channel
|
|
||||||
|
|
||||||
2.4.1 Channel Operators
|
|
||||||
|
|
||||||
The channel operators (also referred to as a "chop" or "chanop") on a
|
|
||||||
given channel are considered to 'own' that channel. Ownership of a
|
|
||||||
channel is shared among channel operators.
|
|
||||||
|
|
||||||
Channel operators are identified by the '@' symbol next to their
|
|
||||||
nickname whenever it is associated with a channel (i.e., replies to
|
|
||||||
the NAMES, WHO and WHOIS commands).
|
|
||||||
|
|
||||||
Since channels starting with the character '+' as prefix do not
|
|
||||||
support channel modes, no member can therefore have the status of
|
|
||||||
channel operator.
|
|
||||||
|
|
||||||
2.4.2 Channel Creator
|
|
||||||
|
|
||||||
A user who creates a channel with the character '!' as prefix is
|
|
||||||
identified as the "channel creator". Upon creation of the channel,
|
|
||||||
this user is also given channel operator status.
|
|
||||||
|
|
||||||
In recognition of this status, the channel creators are endowed with
|
|
||||||
the ability to toggle certain modes of the channel which channel
|
|
||||||
operators may not manipulate.
|
|
||||||
|
|
||||||
A "channel creator" can be distinguished from a channel operator by
|
|
||||||
issuing the proper MODE command. See the "IRC Client Protocol"
|
|
||||||
[IRC-CLIENT] for more information on this topic.
|
|
||||||
|
|
||||||
3. Channel lifetime
|
|
||||||
|
|
||||||
In regard to the lifetime of a channel, there are typically two
|
|
||||||
groups of channels: standard channels which prefix is either '&', '#'
|
|
||||||
or '+', and "safe channels" which prefix is '!'.
|
|
||||||
|
|
||||||
3.1 Standard channels
|
|
||||||
|
|
||||||
These channels are created implicitly when the first user joins it,
|
|
||||||
and cease to exist when the last user leaves it. While the channel
|
|
||||||
exists, any client can reference the channel using the name of the
|
|
||||||
channel.
|
|
||||||
|
|
||||||
The user creating a channel automatically becomes channel operator
|
|
||||||
with the notable exception of channels which name is prefixed by the
|
|
||||||
character '+', see section 4 (Channel modes). See section 2.4.1
|
|
||||||
(Channel Operators) for more details on this title.
|
|
||||||
|
|
||||||
In order to avoid the creation of duplicate channels (typically when
|
|
||||||
the IRC network becomes disjoint because of a split between two
|
|
||||||
servers), channel names SHOULD NOT be allowed to be reused by a user
|
|
||||||
if a channel operator (See Section 2.4.1 (Channel Operators)) has
|
|
||||||
recently left the channel because of a network split. If this
|
|
||||||
happens, the channel name is temporarily unavailable. The duration
|
|
||||||
while a channel remains unavailable should be tuned on a per IRC
|
|
||||||
network basis. It is important to note that this prevents local
|
|
||||||
users from creating a channel using the same name, but does not
|
|
||||||
prevent the channel to be recreated by a remote user. The latter
|
|
||||||
typically happens when the IRC network rejoins. Obviously, this
|
|
||||||
mechanism only makes sense for channels which name begins with the
|
|
||||||
character '#', but MAY be used for channels which name begins with
|
|
||||||
the character '+'. This mechanism is commonly known as "Channel
|
|
||||||
Delay".
|
|
||||||
|
|
||||||
3.2 Safe Channels
|
|
||||||
|
|
||||||
Unlike other channels, "safe channels" are not implicitly created. A
|
|
||||||
user wishing to create such a channel MUST request the creation by
|
|
||||||
sending a special JOIN command to the server in which the channel
|
|
||||||
identifier (then unknown) is replaced by the character '!'. The
|
|
||||||
creation process for this type of channel is strictly controlled.
|
|
||||||
The user only chooses part of the channel name (known as the channel
|
|
||||||
"short name"), the server automatically prepends the user provided
|
|
||||||
name with a channel identifier consisting of five (5) characters.
|
|
||||||
The channel name resulting from the combination of these two elements
|
|
||||||
is unique, making the channel safe from abuses based on network
|
|
||||||
splits.
|
|
||||||
|
|
||||||
The user who creates such a channel automatically becomes "channel
|
|
||||||
creator". See section 2.4.2 (Channel Creator) for more details on
|
|
||||||
this title.
|
|
||||||
|
|
||||||
A server MUST NOT allow the creation of a new channel if another
|
|
||||||
channel with the same short name exists; or if another channel with
|
|
||||||
the same short name existed recently AND any of its member(s) left
|
|
||||||
because of a network split. Such channel ceases to exist after last
|
|
||||||
user leaves AND no other member recently left the channel because of
|
|
||||||
a network split.
|
|
||||||
|
|
||||||
Unlike the mechanism described in section 5.2.2 (Channel Delay), in
|
|
||||||
this case, channel names do not become unavailable: these channels
|
|
||||||
may continue to exist after the last user left. Only the user
|
|
||||||
creating the channel becomes "channel creator", users joining an
|
|
||||||
existing empty channel do not automatically become "channel creator"
|
|
||||||
nor "channel operator".
|
|
||||||
|
|
||||||
To ensure the uniqueness of the channel names, the channel identifier
|
|
||||||
created by the server MUST follow specific rules. For more details
|
|
||||||
on this, see section 5.2.1 (Channel Identifier).
|
|
||||||
|
|
||||||
4. Channel Modes
|
|
||||||
|
|
||||||
The various modes available for channels are as follows:
|
|
||||||
|
|
||||||
O - give "channel creator" status;
|
|
||||||
o - give/take channel operator privilege;
|
|
||||||
v - give/take the voice privilege;
|
|
||||||
|
|
||||||
a - toggle the anonymous channel flag;
|
|
||||||
i - toggle the invite-only channel flag;
|
|
||||||
m - toggle the moderated channel;
|
|
||||||
n - toggle the no messages to channel from clients on the
|
|
||||||
outside;
|
|
||||||
q - toggle the quiet channel flag;
|
|
||||||
p - toggle the private channel flag;
|
|
||||||
s - toggle the secret channel flag;
|
|
||||||
r - toggle the server reop channel flag;
|
|
||||||
t - toggle the topic settable by channel operator only flag;
|
|
||||||
|
|
||||||
k - set/remove the channel key (password);
|
|
||||||
l - set/remove the user limit to channel;
|
|
||||||
|
|
||||||
b - set/remove ban mask to keep users out;
|
|
||||||
e - set/remove an exception mask to override a ban mask;
|
|
||||||
I - set/remove an invitation mask to automatically override
|
|
||||||
the invite-only flag;
|
|
||||||
|
|
||||||
Unless mentioned otherwise below, all these modes can be manipulated
|
|
||||||
by "channel operators" by using the MODE command defined in "IRC
|
|
||||||
Client Protocol" [IRC-CLIENT].
|
|
||||||
|
|
||||||
4.1 Member Status
|
|
||||||
|
|
||||||
The modes in this category take a channel member nickname as argument
|
|
||||||
and affect the privileges given to this user.
|
|
||||||
|
|
||||||
4.1.1 "Channel Creator" Status
|
|
||||||
|
|
||||||
The mode 'O' is only used in conjunction with "safe channels" and
|
|
||||||
SHALL NOT be manipulated by users. Servers use it to give the user
|
|
||||||
creating the channel the status of "channel creator".
|
|
||||||
|
|
||||||
4.1.2 Channel Operator Status
|
|
||||||
|
|
||||||
The mode 'o' is used to toggle the operator status of a channel
|
|
||||||
member.
|
|
||||||
|
|
||||||
4.1.3 Voice Privilege
|
|
||||||
|
|
||||||
The mode 'v' is used to give and take voice privilege to/from a
|
|
||||||
channel member. Users with this privilege can talk on moderated
|
|
||||||
channels. (See section 4.2.3 (Moderated Channel Flag).
|
|
||||||
|
|
||||||
4.2 Channel Flags
|
|
||||||
|
|
||||||
The modes in this category are used to define properties which
|
|
||||||
affects how channels operate.
|
|
||||||
|
|
||||||
4.2.1 Anonymous Flag
|
|
||||||
|
|
||||||
The channel flag 'a' defines an anonymous channel. This means that
|
|
||||||
when a message sent to the channel is sent by the server to users,
|
|
||||||
and the origin is a user, then it MUST be masked. To mask the
|
|
||||||
message, the origin is changed to "anonymous!anonymous@anonymous."
|
|
||||||
(e.g., a user with the nickname "anonymous", the username "anonymous"
|
|
||||||
and from a host called "anonymous."). Because of this, servers MUST
|
|
||||||
forbid users from using the nickname "anonymous". Servers MUST also
|
|
||||||
NOT send QUIT messages for users leaving such channels to the other
|
|
||||||
channel members but generate a PART message instead.
|
|
||||||
|
|
||||||
On channels with the character '&' as prefix, this flag MAY be
|
|
||||||
toggled by channel operators, but on channels with the character '!'
|
|
||||||
as prefix, this flag can be set (but SHALL NOT be unset) by the
|
|
||||||
"channel creator" only. This flag MUST NOT be made available on
|
|
||||||
other types of channels.
|
|
||||||
|
|
||||||
Replies to the WHOIS, WHO and NAMES commands MUST NOT reveal the
|
|
||||||
presence of other users on channels for which the anonymous flag is
|
|
||||||
set.
|
|
||||||
|
|
||||||
4.2.2 Invite Only Flag
|
|
||||||
|
|
||||||
When the channel flag 'i' is set, new members are only accepted if
|
|
||||||
their mask matches Invite-list (See section 4.3.2) or they have been
|
|
||||||
invited by a channel operator. This flag also restricts the usage of
|
|
||||||
the INVITE command (See "IRC Client Protocol" [IRC-CLIENT]) to
|
|
||||||
channel operators.
|
|
||||||
|
|
||||||
4.2.3 Moderated Channel Flag
|
|
||||||
|
|
||||||
The channel flag 'm' is used to control who may speak on a channel.
|
|
||||||
When it is set, only channel operators, and members who have been
|
|
||||||
given the voice privilege may send messages to the channel.
|
|
||||||
|
|
||||||
This flag only affects users.
|
|
||||||
|
|
||||||
4.2.4 No Messages To Channel From Clients On The Outside
|
|
||||||
|
|
||||||
When the channel flag 'n' is set, only channel members MAY send
|
|
||||||
messages to the channel.
|
|
||||||
|
|
||||||
This flag only affects users.
|
|
||||||
|
|
||||||
4.2.5 Quiet Channel
|
|
||||||
|
|
||||||
The channel flag 'q' is for use by servers only. When set, it
|
|
||||||
restricts the type of data sent to users about the channel
|
|
||||||
operations: other user joins, parts and nick changes are not sent.
|
|
||||||
From a user's point of view, the channel contains only one user.
|
|
||||||
|
|
||||||
This is typically used to create special local channels on which the
|
|
||||||
server sends notices related to its operations. This was used as a
|
|
||||||
more efficient and flexible way to replace the user mode 's' defined
|
|
||||||
in RFC 1459 [IRC].
|
|
||||||
|
|
||||||
4.2.6 Private and Secret Channels
|
|
||||||
|
|
||||||
The channel flag 'p' is used to mark a channel "private" and the
|
|
||||||
channel flag 's' to mark a channel "secret". Both properties are
|
|
||||||
similar and conceal the existence of the channel from other users.
|
|
||||||
|
|
||||||
This means that there is no way of getting this channel's name from
|
|
||||||
the server without being a member. In other words, these channels
|
|
||||||
MUST be omitted from replies to queries like the WHOIS command.
|
|
||||||
|
|
||||||
When a channel is "secret", in addition to the restriction above, the
|
|
||||||
server will act as if the channel does not exist for queries like the
|
|
||||||
TOPIC, LIST, NAMES commands. Note that there is one exception to
|
|
||||||
this rule: servers will correctly reply to the MODE command.
|
|
||||||
Finally, secret channels are not accounted for in the reply to the
|
|
||||||
LUSERS command (See "Internet Relay Chat: Client Protocol" [IRC-
|
|
||||||
CLIENT]) when the <mask> parameter is specified.
|
|
||||||
|
|
||||||
The channel flags 'p' and 's' MUST NOT both be set at the same time.
|
|
||||||
If a MODE message originating from a server sets the flag 'p' and the
|
|
||||||
flag 's' is already set for the channel, the change is silently
|
|
||||||
ignored. This should only happen during a split healing phase
|
|
||||||
(mentioned in the "IRC Server Protocol" document [IRC-SERVER]).
|
|
||||||
|
|
||||||
4.2.7 Server Reop Flag
|
|
||||||
|
|
||||||
The channel flag 'r' is only available on channels which name begins
|
|
||||||
with the character '!' and MAY only be toggled by the "channel
|
|
||||||
creator".
|
|
||||||
|
|
||||||
This flag is used to prevent a channel from having no channel
|
|
||||||
operator for an extended period of time. When this flag is set, any
|
|
||||||
channel that has lost all its channel operators for longer than the
|
|
||||||
"reop delay" period triggers a mechanism in servers to reop some or
|
|
||||||
all of the channel inhabitants. This mechanism is described more in
|
|
||||||
detail in section 5.2.4 (Channel Reop Mechanism).
|
|
||||||
|
|
||||||
4.2.8 Topic
|
|
||||||
|
|
||||||
The channel flag 't' is used to restrict the usage of the TOPIC
|
|
||||||
command to channel operators.
|
|
||||||
|
|
||||||
4.2.9 User Limit
|
|
||||||
|
|
||||||
A user limit may be set on channels by using the channel flag 'l'.
|
|
||||||
When the limit is reached, servers MUST forbid their local users to
|
|
||||||
join the channel.
|
|
||||||
|
|
||||||
The value of the limit MUST only be made available to the channel
|
|
||||||
members in the reply sent by the server to a MODE query.
|
|
||||||
|
|
||||||
4.2.10 Channel Key
|
|
||||||
|
|
||||||
When a channel key is set (by using the mode 'k'), servers MUST
|
|
||||||
reject their local users request to join the channel unless this key
|
|
||||||
is given.
|
|
||||||
|
|
||||||
The channel key MUST only be made visible to the channel members in
|
|
||||||
the reply sent by the server to a MODE query.
|
|
||||||
|
|
||||||
4.3 Channel Access Control
|
|
||||||
|
|
||||||
The last category of modes is used to control access to the channel,
|
|
||||||
they take a mask as argument.
|
|
||||||
|
|
||||||
In order to reduce the size of the global database for control access
|
|
||||||
modes set for channels, servers MAY put a maximum limit on the number
|
|
||||||
of such modes set for a particular channel. If such restriction is
|
|
||||||
imposed, it MUST only affect user requests. The limit SHOULD be
|
|
||||||
homogeneous on a per IRC network basis.
|
|
||||||
|
|
||||||
4.3.1 Channel Ban and Exception
|
|
||||||
|
|
||||||
When a user requests to join a channel, his local server checks if
|
|
||||||
the user's address matches any of the ban masks set for the channel.
|
|
||||||
If a match is found, the user request is denied unless the address
|
|
||||||
also matches an exception mask set for the channel.
|
|
||||||
|
|
||||||
Servers MUST NOT allow a channel member who is banned from the
|
|
||||||
channel to speak on the channel, unless this member is a channel
|
|
||||||
operator or has voice privilege. (See Section 4.1.3 (Voice
|
|
||||||
Privilege)).
|
|
||||||
|
|
||||||
A user who is banned from a channel and who carries an invitation
|
|
||||||
sent by a channel operator is allowed to join the channel.
|
|
||||||
|
|
||||||
4.3.2 Channel Invitation
|
|
||||||
|
|
||||||
For channels which have the invite-only flag set (See Section 4.2.2
|
|
||||||
(Invite Only Flag)), users whose address matches an invitation mask
|
|
||||||
set for the channel are allowed to join the channel without any
|
|
||||||
invitation.
|
|
||||||
|
|
||||||
5. Current Implementations
|
|
||||||
|
|
||||||
The only current implementation of these rules as part of the IRC
|
|
||||||
protocol is the IRC server, version 2.10.
|
|
||||||
|
|
||||||
The rest of this section deals with issues that are mostly of
|
|
||||||
importance to those who wish to implement a server but some parts may
|
|
||||||
also be of interest for client writers.
|
|
||||||
|
|
||||||
5.1 Tracking Recently Used Channels
|
|
||||||
|
|
||||||
This mechanism is commonly known as "Channel Delay" and generally
|
|
||||||
only applies to channels which names is prefixed with the character
|
|
||||||
'#' (See Section 3.1 "Standard channels").
|
|
||||||
|
|
||||||
When a network split occurs, servers SHOULD keep track of which
|
|
||||||
channels lost a "channel operator" as the result of the break. These
|
|
||||||
channels are then in a special state which lasts for a certain period
|
|
||||||
of time. In this particular state, the channels cannot cease to
|
|
||||||
|
|
||||||
exist. If all the channel members leave the channel, the channel
|
|
||||||
becomes unavailable: the server local clients cannot join the channel
|
|
||||||
as long as it is empty.
|
|
||||||
|
|
||||||
Once a channel is unavailable, it will become available again either
|
|
||||||
because a remote user has joined the channel (most likely because the
|
|
||||||
network is healing), or because the delay period has expired (in
|
|
||||||
which case the channel ceases to exist and may be re-created).
|
|
||||||
|
|
||||||
The duration for which a channel death is delayed SHOULD be set
|
|
||||||
considering many factors among which are the size (user wise) of the
|
|
||||||
IRC network, and the usual duration of network splits. It SHOULD be
|
|
||||||
uniform on all servers for a given IRC network.
|
|
||||||
|
|
||||||
5.2 Safe Channels
|
|
||||||
|
|
||||||
This document introduces the notion of "safe channels". These
|
|
||||||
channels have a name prefixed with the character '!' and great effort
|
|
||||||
is made to avoid collisions in this name space. Collisions are not
|
|
||||||
impossible, however they are very unlikely.
|
|
||||||
|
|
||||||
5.2.1 Channel Identifier
|
|
||||||
|
|
||||||
The channel identifier is a function of the time. The current time
|
|
||||||
(as defined under UNIX by the number of seconds elapsed since
|
|
||||||
00:00:00 GMT, January 1, 1970) is converted in a string of five (5)
|
|
||||||
characters using the following base:
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" (each character has a decimal
|
|
||||||
value starting from 0 for 'A' to 35 for '0').
|
|
||||||
|
|
||||||
The channel identifier therefore has a periodicity of 36^5 seconds
|
|
||||||
(about 700 days).
|
|
||||||
|
|
||||||
5.2.2 Channel Delay
|
|
||||||
|
|
||||||
These channels MUST be subject to the "channel delay" mechanism
|
|
||||||
described in section 5.1 (Channel Delay). However, the mechanism is
|
|
||||||
slightly adapted to fit better.
|
|
||||||
|
|
||||||
Servers MUST keep track of all such channels which lose members as
|
|
||||||
the result of a network split, no matter whether the user is a
|
|
||||||
"channel operator" or not.
|
|
||||||
|
|
||||||
However, these channels do NOT ever become unavailable, it is always
|
|
||||||
possible to join them even when they are empty.
|
|
||||||
|
|
||||||
5.2.3 Abuse Window
|
|
||||||
|
|
||||||
Because the periodicity is so long, attacks on a particular channel
|
|
||||||
(name) may only occur once in a very long while. However, with luck
|
|
||||||
and patience, it is still possible for a user to cause a channel
|
|
||||||
collision. In order to avoid this, servers MUST "look in the future"
|
|
||||||
and keep a list of channel names which identifier is about to be used
|
|
||||||
(in the coming few days for example). Such list should remain small,
|
|
||||||
not be a burden for servers to maintain and be used to avoid channel
|
|
||||||
collisions by preventing the re-creation of such channel for a longer
|
|
||||||
period of time than channel delay does.
|
|
||||||
|
|
||||||
Eventually a server MAY choose to extend this procedure to forbid
|
|
||||||
creation of channels with the same shortname only (then ignoring the
|
|
||||||
channel identifier).
|
|
||||||
|
|
||||||
5.2.4 Preserving Sanity In The Name Space
|
|
||||||
|
|
||||||
The combination of the mechanisms described in sections 5.2.2 and
|
|
||||||
5.2.3 makes it quite difficult for a user to create a channel
|
|
||||||
collision. However, another type of abuse consists of creating many
|
|
||||||
channels having the same shortname, but different identifiers. To
|
|
||||||
prevent this from happening, servers MUST forbid the creation of a
|
|
||||||
new channel which has the same shortname of a channel currently
|
|
||||||
existing.
|
|
||||||
|
|
||||||
5.2.5 Server Reop Mechanism
|
|
||||||
|
|
||||||
When a channel has been opless for longer than the "reop delay"
|
|
||||||
period and has the channel flag 'r' set (See Section 4.2.7 (Server
|
|
||||||
Reop Flag)), IRC servers are responsible for giving the channel
|
|
||||||
operator status randomly to some of the members.
|
|
||||||
|
|
||||||
The exact logic used for this mechanism by the current implementation
|
|
||||||
is described below. Servers MAY use a different logic, but that it
|
|
||||||
is strongly RECOMMENDED that all servers use the same logic on a
|
|
||||||
particular IRC network to maintain coherence as well as fairness.
|
|
||||||
For the same reason, the "reop delay" SHOULD be uniform on all
|
|
||||||
servers for a given IRC network. As for the "channel delay", the
|
|
||||||
value of the "reop delay" SHOULD be set considering many factors
|
|
||||||
among which are the size (user wise) of the IRC network, and the
|
|
||||||
usual duration of network splits.
|
|
||||||
|
|
||||||
a) the reop mechanism is triggered after a random time following the
|
|
||||||
expiration of the "reop delay". This should limit the eventuality
|
|
||||||
of the mechanism being triggered at the same time (for the same
|
|
||||||
channel) on two separate servers.
|
|
||||||
|
|
||||||
b) If the channel is small (five (5) users or less), and the "channel
|
|
||||||
delay" for this channel has expired,
|
|
||||||
Then reop all channel members if at least one member is local to
|
|
||||||
the server.
|
|
||||||
|
|
||||||
c) If the channel is small (five (5) users or less), and the "channel
|
|
||||||
delay" for this channel has expired, and the "reop delay" has
|
|
||||||
expired for longer than its value,
|
|
||||||
Then reop all channel members.
|
|
||||||
|
|
||||||
d) For other cases, reop at most one member on the channel, based on
|
|
||||||
some method build into the server. If you don't reop a member, the
|
|
||||||
method should be such that another server will probably op
|
|
||||||
someone. The method SHOULD be the same over the whole network. A
|
|
||||||
good heuristic could be just random reop.
|
|
||||||
(The current implementation actually tries to choose a member
|
|
||||||
local to the server who has not been idle for too long, eventually
|
|
||||||
postponing action, therefore letting other servers have a chance
|
|
||||||
to find a "not too idle" member. This is over complicated due to
|
|
||||||
the fact that servers only know the "idle" time of their local
|
|
||||||
users)
|
|
||||||
|
|
||||||
6. Current problems
|
|
||||||
|
|
||||||
There are a number of recognized problems with the way IRC channels
|
|
||||||
are managed. Some of these can be directly attributed to the rules
|
|
||||||
defined in this document, while others are the result of the
|
|
||||||
underlying "IRC Server Protocol" [IRC-SERVER]. Although derived from
|
|
||||||
RFC 1459 [IRC], this document introduces several novelties in an
|
|
||||||
attempt to solve some of the known problems.
|
|
||||||
|
|
||||||
6.1 Labels
|
|
||||||
|
|
||||||
This document defines one of the many labels used by the IRC
|
|
||||||
protocol. Although there are several distinct namespaces (based on
|
|
||||||
the channel name prefix), duplicates inside each of these are not
|
|
||||||
allowed. Currently, it is possible for users on different servers to
|
|
||||||
pick the label which may result in collisions (with the exception of
|
|
||||||
channels known to only one server where they can be averted).
|
|
||||||
|
|
||||||
6.1.1 Channel Delay
|
|
||||||
|
|
||||||
The channel delay mechanism described in section 5.1 (Tracking
|
|
||||||
Recently Used Channels) and used for channels prefixed with the
|
|
||||||
character '#' is a simple attempt at preventing collisions from
|
|
||||||
happening. Experience has shown that, under normal circumstances, it
|
|
||||||
|
|
||||||
is very efficient; however, it obviously has severe limitations
|
|
||||||
keeping it from being an adequate solution to the problem discussed
|
|
||||||
here.
|
|
||||||
|
|
||||||
6.1.2 Safe Channels
|
|
||||||
|
|
||||||
"Safe channels" described in section 3.2 (Safe Channels) are a better
|
|
||||||
way to prevent collisions from happening as it prevents users from
|
|
||||||
having total control over the label they choose. The obvious
|
|
||||||
drawback for such labels is that they are not user friendly.
|
|
||||||
However, it is fairly trivial for a client program to improve on
|
|
||||||
this.
|
|
||||||
|
|
||||||
6.2 Mode Propagation Delays
|
|
||||||
|
|
||||||
Because of network delays induced by the network, and because each
|
|
||||||
server on the path is REQUIRED to check the validity of mode changes
|
|
||||||
(e.g., user exists and has the right privileges), it is not unusual
|
|
||||||
for a MODE message to only affect part of the network, often creating
|
|
||||||
a discrepancy between servers on the current state of a channel.
|
|
||||||
|
|
||||||
While this may seem easy to fix (by having only the original server
|
|
||||||
check the validity of mode changes), it was decided not to do so for
|
|
||||||
various reasons. One concern is that servers cannot trust each
|
|
||||||
other, and that a misbehaving servers can easily be detected. This
|
|
||||||
way of doing so also stops wave effects on channels which are out of
|
|
||||||
synch when mode changes are issued from different directions.
|
|
||||||
|
|
||||||
6.3 Collisions And Channel Modes
|
|
||||||
|
|
||||||
The "Internet Relay Chat: Server Protocol" document [IRC-SERVER]
|
|
||||||
describes how channel data is exchanged when two servers connect to
|
|
||||||
each other. Channel collisions (either legitimate or not) are
|
|
||||||
treated as inclusive events, meaning that the resulting channel has
|
|
||||||
for members all the users who are members of the channel on either
|
|
||||||
server prior to the connection.
|
|
||||||
|
|
||||||
Similarly, each server sends the channel modes to the other one.
|
|
||||||
Therefore, each server also receives these channel modes. There are
|
|
||||||
three types of modes for a given channel: flags, masks, and data.
|
|
||||||
The first two types are easy to deal with as they are either set or
|
|
||||||
unset. If such a mode is set on one server, it MUST be set on the
|
|
||||||
other server as a result of the connection.
|
|
||||||
|
|
||||||
As topics are not sent as part of this exchange, they are not a
|
|
||||||
problem. However, channel modes 'l' and 'k' are exchanged, and if
|
|
||||||
they are set on both servers prior to the connection, there is no
|
|
||||||
mechanism to decide which of the two values takes precedence. It is
|
|
||||||
left up to the users to fix the resulting discrepancy.
|
|
||||||
|
|
||||||
6.4 Resource Exhaustion
|
|
||||||
|
|
||||||
The mode based on masks defined in section 4.3 make the IRC servers
|
|
||||||
(and network) vulnerable to a simple abuse of the system: a single
|
|
||||||
channel operator can set as many different masks as possible on a
|
|
||||||
particular channel. This can easily cause the server to waste
|
|
||||||
memory, as well as network bandwidth (since the info is propagated to
|
|
||||||
other servers). For this reason it is RECOMMENDED that a limit be
|
|
||||||
put on the number of such masks per channels as mentioned in section
|
|
||||||
4.3.
|
|
||||||
|
|
||||||
Moreover, more complex mechanisms MAY be used to avoid having
|
|
||||||
redundant masks set for the same channel.
|
|
||||||
|
|
||||||
7. Security Considerations
|
|
||||||
|
|
||||||
7.1 Access Control
|
|
||||||
|
|
||||||
One of the main ways to control access to a channel is to use masks
|
|
||||||
which are based on the username and hostname of the user connections.
|
|
||||||
This mechanism can only be efficient and safe if the IRC servers have
|
|
||||||
an accurate way of authenticating user connections, and if users
|
|
||||||
cannot easily get around it. While it is in theory possible to
|
|
||||||
implement such a strict authentication mechanism, most IRC networks
|
|
||||||
(especially public networks) do not have anything like this in place
|
|
||||||
and provide little guaranty about the accuracy of the username and
|
|
||||||
hostname for a particular client connection.
|
|
||||||
|
|
||||||
Another way to control access is to use a channel key, but since this
|
|
||||||
key is sent in plaintext, it is vulnerable to traditional man in the
|
|
||||||
middle attacks.
|
|
||||||
|
|
||||||
7.2 Channel Privacy
|
|
||||||
|
|
||||||
Because channel collisions are treated as inclusive events (See
|
|
||||||
Section 6.3), it is possible for users to join a channel overriding
|
|
||||||
its access control settings. This method has long been used by
|
|
||||||
individuals to "take over" channels by "illegitimately" gaining
|
|
||||||
channel operator status on the channel. The same method can be used
|
|
||||||
to find out the exact list of members of a channel, as well as to
|
|
||||||
eventually receive some of the messages sent to the channel.
|
|
||||||
|
|
||||||
7.3 Anonymity
|
|
||||||
|
|
||||||
The anonymous channel flag (See Section 4.2.1) can be used to render
|
|
||||||
all users on such channel "anonymous" by presenting all messages to
|
|
||||||
the channel as originating from a pseudo user which nickname is
|
|
||||||
"anonymous". This is done at the client-server level, and no
|
|
||||||
anonymity is provided at the server-server level.
|
|
||||||
|
|
||||||
It should be obvious to readers, that the level of anonymity offered
|
|
||||||
is quite poor and insecure, and that clients SHOULD display strong
|
|
||||||
warnings for users joining such channels.
|
|
||||||
|
|
||||||
8. Current support and availability
|
|
||||||
|
|
||||||
Mailing lists for IRC related discussion:
|
|
||||||
General discussion: ircd-users@irc.org
|
|
||||||
Protocol development: ircd-dev@irc.org
|
|
||||||
|
|
||||||
Software implementations:
|
|
||||||
ftp://ftp.irc.org/irc/server
|
|
||||||
ftp://ftp.funet.fi/pub/unix/irc
|
|
||||||
ftp://coombs.anu.edu.au/pub/irc
|
|
||||||
|
|
||||||
Newsgroup: alt.irc
|
|
||||||
|
|
||||||
9. Acknowledgements
|
|
||||||
|
|
||||||
Parts of this document were copied from the RFC 1459 [IRC] which
|
|
||||||
first formally documented the IRC Protocol. It has also benefited
|
|
||||||
from many rounds of review and comments. In particular, the
|
|
||||||
following people have made significant contributions to this
|
|
||||||
document:
|
|
||||||
|
|
||||||
Matthew Green, Michael Neumayer, Volker Paulsen, Kurt Roeckx, Vesa
|
|
||||||
Ruokonen, Magnus Tjernstrom, Stefan Zehl.
|
|
||||||
|
|
||||||
10. References
|
|
||||||
|
|
||||||
[KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
|
|
||||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
|
||||||
|
|
||||||
[IRC] Oikarinen, J. and D. Reed, "Internet Relay Chat
|
|
||||||
Protocol", RFC 1459, May 1993.
|
|
||||||
|
|
||||||
[IRC-ARCH] Kalt, C., "Internet Relay Chat: Architecture", RFC 2810,
|
|
||||||
April 2000.
|
|
||||||
|
|
||||||
[IRC-CLIENT] Kalt, C., "Internet Relay Chat: Client Protocol", RFC
|
|
||||||
2812, April 2000.
|
|
||||||
|
|
||||||
[IRC-SERVER] Kalt, C., "Internet Relay Chat: Server Protocol", RFC
|
|
||||||
2813, April 2000.
|
|
||||||
|
|
||||||
11. Author's Address
|
|
||||||
|
|
||||||
Christophe Kalt
|
|
||||||
99 Teaneck Rd, Apt #117
|
|
||||||
Ridgefield Park, NJ 07660
|
|
||||||
USA
|
|
||||||
|
|
||||||
EMail: kalt@stealth.net
|
|
||||||
|
|
||||||
12. Full Copyright Statement
|
|
||||||
|
|
||||||
Copyright (C) The Internet Society (2000). All Rights Reserved.
|
|
||||||
|
|
||||||
This document and translations of it may be copied and furnished to
|
|
||||||
others, and derivative works that comment on or otherwise explain it
|
|
||||||
or assist in its implementation may be prepared, copied, published
|
|
||||||
and distributed, in whole or in part, without restriction of any
|
|
||||||
kind, provided that the above copyright notice and this paragraph are
|
|
||||||
included on all such copies and derivative works. However, this
|
|
||||||
document itself may not be modified in any way, such as by removing
|
|
||||||
the copyright notice or references to the Internet Society or other
|
|
||||||
Internet organizations, except as needed for the purpose of
|
|
||||||
developing Internet standards in which case the procedures for
|
|
||||||
copyrights defined in the Internet Standards process must be
|
|
||||||
followed, or as required to translate it into languages other than
|
|
||||||
English.
|
|
||||||
|
|
||||||
The limited permissions granted above are perpetual and will not be
|
|
||||||
revoked by the Internet Society or its successors or assigns.
|
|
||||||
|
|
||||||
This document and the information contained herein is provided on an
|
|
||||||
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
|
|
||||||
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
||||||
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
|
|
||||||
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
Acknowledgement
|
|
||||||
|
|
||||||
Funding for the RFC Editor function is currently provided by the
|
|
||||||
Internet Society.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Previous: RFC 2810 - Internet Relay Next: RFC 2812 - Internet Relay Chat:
|
|
||||||
Chat: Architecture Client Protocol
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
|
||||||
|
|
||||||
[ RFC Index | RFC Search | Usenet FAQs | Web FAQs | Documents | Cities ]
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
4
plugins/8ball.py
Normal file → Executable file
4
plugins/8ball.py
Normal file → Executable file
|
@ -33,9 +33,11 @@ answers = [g + "As I see it, yes",
|
||||||
r + "Outlook not so good",
|
r + "Outlook not so good",
|
||||||
r + "Very doubtful"]
|
r + "Very doubtful"]
|
||||||
|
|
||||||
|
|
||||||
@hook.command('8ball')
|
@hook.command('8ball')
|
||||||
def eightball(inp, me=None):
|
def eightball(inp, me=None):
|
||||||
".8ball <question> -- The all knowing magic eight ball, in electronic form. Ask and it shall be answered!"
|
".8ball <question> -- The all knowing magic eight ball, "\
|
||||||
|
"in electronic form. Ask and it shall be answered!"
|
||||||
global nextresponsenumber
|
global nextresponsenumber
|
||||||
inp = inp.strip()
|
inp = inp.strip()
|
||||||
if re.match("[a-zA-Z0-9]", inp[-1]):
|
if re.match("[a-zA-Z0-9]", inp[-1]):
|
||||||
|
|
59
plugins/admin.py
Normal file → Executable file
59
plugins/admin.py
Normal file → Executable file
|
@ -1,22 +1,52 @@
|
||||||
# Shitty plugin made by iloveportalz0r
|
# Shitty plugin made by iloveportalz0r
|
||||||
# Broken by The Noodle
|
# Broken by The Noodle
|
||||||
# Improved by Lukeroge
|
# Improved by Lukeroge
|
||||||
|
# Further improved by neersighted
|
||||||
from util import hook
|
from util import hook
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@hook.command
|
@hook.command("quit", autohelp=False)
|
||||||
def quit(inp, input=None, db=None, notice=None):
|
@hook.command("exit", autohelp=False)
|
||||||
".quit [reason] -- Kills the bot, with [reason] reason as its quit message.."
|
@hook.command(autohelp=False)
|
||||||
|
def stop(inp, input=None, db=None, notice=None):
|
||||||
|
".stop [reason] -- Kills the bot, with [reason] as its quit message."
|
||||||
if not input.nick in input.bot.config["admins"]:
|
if not input.nick in input.bot.config["admins"]:
|
||||||
notice("Only bot admins can use this command!")
|
notice("Only bot admins can use this command!")
|
||||||
return
|
return
|
||||||
if inp:
|
if inp:
|
||||||
input.conn.send("QUIT :Bot killed by "+input.nick+" (" + inp + ")")
|
input.conn.send("QUIT :Killed by " + input.nick + " (" + inp + ")")
|
||||||
else:
|
else:
|
||||||
input.conn.send("QUIT :Bot killed by "+input.nick+" (no reason)")
|
input.conn.send("QUIT :Killed by " + input.nick + " (no reason)")
|
||||||
time.sleep(3)
|
time.sleep(5)
|
||||||
sys.exit()
|
subprocess.call("./cloudbot stop", shell=True)
|
||||||
|
|
||||||
|
|
||||||
|
@hook.command("reboot", autohelp=False)
|
||||||
|
@hook.command(autohelp=False)
|
||||||
|
def restart(inp, input=None, db=None, notice=None):
|
||||||
|
".restart [reason] -- Restarts the bot, with [reason] as its quit message."
|
||||||
|
if not input.nick in input.bot.config["admins"]:
|
||||||
|
notice("Only bot admins can use this command!")
|
||||||
|
return
|
||||||
|
if inp:
|
||||||
|
input.conn.send("QUIT :Restarted by " + input.nick + " (" + inp + ")")
|
||||||
|
else:
|
||||||
|
input.conn.send("QUIT :Restarted by " + input.nick + " (no reason)")
|
||||||
|
time.sleep(5)
|
||||||
|
os.execl("./cloudbot", "restart")
|
||||||
|
|
||||||
|
@hook.command("clearlogs", autohelp=False)
|
||||||
|
@hook.command(autohelp=False)
|
||||||
|
def clear(inp, input=None, db=None, notice=None):
|
||||||
|
".clear -- Clears the bot's log(s)."
|
||||||
|
if not input.nick in input.bot.config["admins"]:
|
||||||
|
notice("Only bot admins can use this command!")
|
||||||
|
return
|
||||||
|
time.sleep(5)
|
||||||
|
subprocess.call("./cloudbot clear", shell=True)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
|
@ -39,6 +69,7 @@ def cycle(inp, input=None, db=None, notice=None):
|
||||||
input.conn.send("PART " + inp)
|
input.conn.send("PART " + inp)
|
||||||
input.conn.send("JOIN " + inp)
|
input.conn.send("JOIN " + inp)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def part(inp, input=None, notice=None):
|
def part(inp, input=None, notice=None):
|
||||||
".part <channel> -- Parts from <channel>."
|
".part <channel> -- Parts from <channel>."
|
||||||
|
@ -48,6 +79,7 @@ def part(inp, input=None, notice=None):
|
||||||
notice("Attempting to part from " + inp + "...")
|
notice("Attempting to part from " + inp + "...")
|
||||||
input.conn.send("PART " + inp)
|
input.conn.send("PART " + inp)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def nick(inp, input=None, notice=None):
|
def nick(inp, input=None, notice=None):
|
||||||
".nick <nick> -- Changes the bots nickname to <nick>."
|
".nick <nick> -- Changes the bots nickname to <nick>."
|
||||||
|
@ -57,6 +89,7 @@ def nick(inp, input=None, notice=None):
|
||||||
notice("Changing nick to " + inp + ".")
|
notice("Changing nick to " + inp + ".")
|
||||||
input.conn.send("NICK " + inp)
|
input.conn.send("NICK " + inp)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def raw(inp, input=None, notice=None):
|
def raw(inp, input=None, notice=None):
|
||||||
".raw <command> -- Sends a RAW IRC command."
|
".raw <command> -- Sends a RAW IRC command."
|
||||||
|
@ -66,6 +99,7 @@ def raw(inp, input=None, notice=None):
|
||||||
notice("Raw command sent.")
|
notice("Raw command sent.")
|
||||||
input.conn.send(inp)
|
input.conn.send(inp)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def kick(inp, input=None, notice=None):
|
def kick(inp, input=None, notice=None):
|
||||||
".kick [channel] <user> [reason] -- kicks a user."
|
".kick [channel] <user> [reason] -- kicks a user."
|
||||||
|
@ -97,9 +131,12 @@ def kick(inp, input=None, notice=None):
|
||||||
notice("Attempting to kick %s from %s..." % (user, chan))
|
notice("Attempting to kick %s from %s..." % (user, chan))
|
||||||
input.conn.send(out)
|
input.conn.send(out)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def say(inp, input=None, notice=None):
|
def say(inp, input=None, notice=None):
|
||||||
".say [channel] <message> -- Makes the bot say <message> in [channel]. If [channel] is blank the bot will say the <message> in the channel the command was used in."
|
".say [channel] <message> -- Makes the bot say <message> in [channel]. "\
|
||||||
|
"If [channel] is blank the bot will say the <message> in "\
|
||||||
|
"the channel the command was used in."
|
||||||
if not input.nick in input.bot.config["admins"]:
|
if not input.nick in input.bot.config["admins"]:
|
||||||
notice("Only bot admins can use this command!")
|
notice("Only bot admins can use this command!")
|
||||||
return
|
return
|
||||||
|
@ -118,10 +155,13 @@ def say(inp, input=None, notice=None):
|
||||||
out = "PRIVMSG %s :%s" % (input.chan, message)
|
out = "PRIVMSG %s :%s" % (input.chan, message)
|
||||||
input.conn.send(out)
|
input.conn.send(out)
|
||||||
|
|
||||||
|
|
||||||
@hook.command("me")
|
@hook.command("me")
|
||||||
@hook.command
|
@hook.command
|
||||||
def act(inp, input=None, notice=None):
|
def act(inp, input=None, notice=None):
|
||||||
".act [channel] <action> -- Makes the bot act out <action> in [channel]. Ff [channel] is blank the bot will act the <action> in the channel the command was used in."
|
".act [channel] <action> -- Makes the bot act out <action> in [channel] "\
|
||||||
|
"If [channel] is blank the bot will act the <action> in "\
|
||||||
|
"the channel the command was used in."
|
||||||
if not input.nick in input.bot.config["admins"]:
|
if not input.nick in input.bot.config["admins"]:
|
||||||
notice("Only bot admins can use this command!")
|
notice("Only bot admins can use this command!")
|
||||||
return
|
return
|
||||||
|
@ -140,6 +180,7 @@ def act(inp, input=None, notice=None):
|
||||||
out = "PRIVMSG %s :\x01ACTION %s\x01" % (input.chan, message)
|
out = "PRIVMSG %s :\x01ACTION %s\x01" % (input.chan, message)
|
||||||
input.conn.send(out)
|
input.conn.send(out)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def topic(inp, input=None, notice=None):
|
def topic(inp, input=None, notice=None):
|
||||||
".topic [channel] <topic> -- Change the topic of a channel."
|
".topic [channel] <topic> -- Change the topic of a channel."
|
||||||
|
|
0
plugins/bf.py
Normal file → Executable file
0
plugins/bf.py
Normal file → Executable file
0
plugins/bitcoin.py
Normal file → Executable file
0
plugins/bitcoin.py
Normal file → Executable file
3
plugins/choose.py
Normal file → Executable file
3
plugins/choose.py
Normal file → Executable file
|
@ -6,7 +6,8 @@ from util import hook
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def choose(inp):
|
def choose(inp):
|
||||||
".choose <choice1>, [choice2], [choice3], [choice4], ... -- Randomly picks one of the given choices."
|
".choose <choice1>, [choice2], [choice3], [choice4], ... -- "\
|
||||||
|
"Randomly picks one of the given choices."
|
||||||
|
|
||||||
c = re.findall(r'([^,]+)', inp)
|
c = re.findall(r'([^,]+)', inp)
|
||||||
if len(c) == 1:
|
if len(c) == 1:
|
||||||
|
|
9
plugins/coin.py
Normal file → Executable file
9
plugins/coin.py
Normal file → Executable file
|
@ -2,6 +2,7 @@
|
||||||
from util import hook
|
from util import hook
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
# used for tails: x heads: y
|
# used for tails: x heads: y
|
||||||
def flip_simple(count):
|
def flip_simple(count):
|
||||||
heads = 0
|
heads = 0
|
||||||
|
@ -18,8 +19,8 @@ def flip_simple(count):
|
||||||
@hook.command(autohelp=False)
|
@hook.command(autohelp=False)
|
||||||
def coin(inp):
|
def coin(inp):
|
||||||
".coin [amount] -- Flips [amount] of coins."
|
".coin [amount] -- Flips [amount] of coins."
|
||||||
|
# checking for valid input. if valid input [count=inp],
|
||||||
# checking for valid input. if valid input [count=inp], if invalid [return error], if no input [count=1]
|
# if invalid [return error], if no input [count=1]
|
||||||
if inp.isdigit():
|
if inp.isdigit():
|
||||||
count = int(inp)
|
count = int(inp)
|
||||||
else:
|
else:
|
||||||
|
@ -40,5 +41,5 @@ def coin(inp):
|
||||||
return "You flip a coin and get " + sidename + "."
|
return "You flip a coin and get " + sidename + "."
|
||||||
else:
|
else:
|
||||||
flips = flip_simple(count)
|
flips = flip_simple(count)
|
||||||
return "You flip %s coins and get %s heads and %s tails." % (str(count), str(flips[0]), str(flips[1]))
|
return "You flip %s coins and get "\
|
||||||
|
"%s heads and %s tails." % (str(count), str(flips[0]), str(flips[1]))
|
||||||
|
|
22
plugins/ctcp.py
Executable file
22
plugins/ctcp.py
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
from util import hook
|
||||||
|
|
||||||
|
|
||||||
|
# CTCP responses
|
||||||
|
@hook.regex(r'^\x01VERSION\x01$')
|
||||||
|
def ctcpversion(inp, notice=None):
|
||||||
|
notice('\x01VERSION: CloudBot - http://git.io/cloudbot')
|
||||||
|
|
||||||
|
|
||||||
|
@hook.regex(r'^\x01PING\x01$')
|
||||||
|
def ctcpping(inp, notice=None):
|
||||||
|
notice('\x01PING: PONG')
|
||||||
|
|
||||||
|
|
||||||
|
@hook.regex(r'^\x01TIME\x01$')
|
||||||
|
def ctcptime(inp, notice=None):
|
||||||
|
notice('\x01TIME: GET A WATCH')
|
||||||
|
|
||||||
|
|
||||||
|
@hook.regex(r'^\x01FINGER\x01$')
|
||||||
|
def ctcpfinger(inp, notice=None):
|
||||||
|
notice('\x01FINGER: WHERE ARE YOU PUTTING THAT')
|
8
plugins/cypher.py
Normal file → Executable file
8
plugins/cypher.py
Normal file → Executable file
|
@ -1,8 +1,6 @@
|
||||||
'''
|
'''
|
||||||
Plugin which (de)cyphers a string
|
Plugin which (de)cyphers a string
|
||||||
|
|
||||||
Doesn't cypher non-alphanumeric strings yet.
|
Doesn't cypher non-alphanumeric strings yet.
|
||||||
|
|
||||||
by instanceoftom
|
by instanceoftom
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -10,6 +8,7 @@ from util import hook
|
||||||
chars = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ "
|
chars = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ "
|
||||||
len_chars = len(chars)
|
len_chars = len(chars)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def cypher(inp):
|
def cypher(inp):
|
||||||
".cypher <pass> <string> -- Cyphers <string> with <password>."
|
".cypher <pass> <string> -- Cyphers <string> with <password>."
|
||||||
|
@ -34,9 +33,9 @@ def cypher(inp):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
out += character
|
out += character
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def decypher(inp):
|
def decypher(inp):
|
||||||
".decypher <pass> <string> -- Decyphers <string> with <password>."
|
".decypher <pass> <string> -- Decyphers <string> with <password>."
|
||||||
|
@ -45,10 +44,7 @@ def decypher(inp):
|
||||||
len_passwd = len(passwd)
|
len_passwd = len(passwd)
|
||||||
inp = " ".join(inp.split(" ")[1:])
|
inp = " ".join(inp.split(" ")[1:])
|
||||||
|
|
||||||
|
|
||||||
passwd_index = 0
|
passwd_index = 0
|
||||||
#I am lazy and I could do the math to get the passwd_index
|
|
||||||
#for this inp, but meh thats for a later day so lets loop.
|
|
||||||
for character in inp:
|
for character in inp:
|
||||||
try:
|
try:
|
||||||
chr_index = chars.index(character)
|
chr_index = chars.index(character)
|
||||||
|
|
0
plugins/dice.py
Normal file → Executable file
0
plugins/dice.py
Normal file → Executable file
1
plugins/dictionary.py
Normal file → Executable file
1
plugins/dictionary.py
Normal file → Executable file
|
@ -22,6 +22,7 @@ def urban(inp):
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
# define plugin by GhettoWizard & Scaevolus
|
# define plugin by GhettoWizard & Scaevolus
|
||||||
@hook.command('dictionary')
|
@hook.command('dictionary')
|
||||||
@hook.command
|
@hook.command
|
||||||
|
|
0
plugins/down.py
Normal file → Executable file
0
plugins/down.py
Normal file → Executable file
1
plugins/drama.py
Normal file → Executable file
1
plugins/drama.py
Normal file → Executable file
|
@ -25,4 +25,3 @@ def drama(inp):
|
||||||
return '%s :: \x02%s\x02' % (summary, url)
|
return '%s :: \x02%s\x02' % (summary, url)
|
||||||
|
|
||||||
return "error"
|
return "error"
|
||||||
|
|
||||||
|
|
2
plugins/fact.py
Normal file → Executable file
2
plugins/fact.py
Normal file → Executable file
|
@ -15,8 +15,6 @@ def fact(inp, say=False, nick=False):
|
||||||
|
|
||||||
return u"%s [ %s ]" % (fact, link)
|
return u"%s [ %s ]" % (fact, link)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_fact():
|
def get_fact():
|
||||||
page = http.get('http://www.omg-facts.com/random')
|
page = http.get('http://www.omg-facts.com/random')
|
||||||
soup = BeautifulSoup(page)
|
soup = BeautifulSoup(page)
|
||||||
|
|
14
plugins/factoids.py
Normal file → Executable file
14
plugins/factoids.py
Normal file → Executable file
|
@ -37,6 +37,7 @@ def multiwordReplace(text, wordDic):
|
||||||
the associated value, return the changed text
|
the associated value, return the changed text
|
||||||
"""
|
"""
|
||||||
rc = re.compile('|'.join(map(re.escape, wordDic)))
|
rc = re.compile('|'.join(map(re.escape, wordDic)))
|
||||||
|
|
||||||
def translate(match):
|
def translate(match):
|
||||||
return wordDic[match.group(0)]
|
return wordDic[match.group(0)]
|
||||||
return rc.sub(translate, text)
|
return rc.sub(translate, text)
|
||||||
|
@ -50,7 +51,6 @@ def remember(inp, nick='', db=None, say=None, input=None, notice=None):
|
||||||
return
|
return
|
||||||
db_init(db)
|
db_init(db)
|
||||||
|
|
||||||
|
|
||||||
append = False
|
append = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -87,6 +87,7 @@ def remember(inp, nick='', db=None, say=None, input=None, notice=None):
|
||||||
notice('Remembered!')
|
notice('Remembered!')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@hook.command("f")
|
@hook.command("f")
|
||||||
def forget(inp, db=None, input=None, notice=None):
|
def forget(inp, db=None, input=None, notice=None):
|
||||||
".forget <word> -- Forgets a remembered <word>."
|
".forget <word> -- Forgets a remembered <word>."
|
||||||
|
@ -107,13 +108,22 @@ def forget(inp, db=None, input=None, notice=None):
|
||||||
notice("I don't know about that.")
|
notice("I don't know about that.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@hook.command("info")
|
@hook.command("info")
|
||||||
@hook.regex(r'^\? ?(.+)')
|
@hook.regex(r'^\? ?(.+)')
|
||||||
def question(inp, say=None, db=None):
|
def question(inp, say=None, db=None, bot=None):
|
||||||
"?<word> -- Shows what data is associated with <word>."
|
"?<word> -- Shows what data is associated with <word>."
|
||||||
|
try:
|
||||||
|
prefix_on = bot.config["plugins"]["factoids"]["prefix"]
|
||||||
|
except KeyError:
|
||||||
|
prefix_on = False
|
||||||
|
|
||||||
db_init(db)
|
db_init(db)
|
||||||
|
|
||||||
data = get_memory(db, inp.group(1).strip())
|
data = get_memory(db, inp.group(1).strip())
|
||||||
if data:
|
if data:
|
||||||
out = multiwordReplace(data, shortcodes)
|
out = multiwordReplace(data, shortcodes)
|
||||||
|
if prefix_on:
|
||||||
|
say("\x02[%s]:\x02 %s" % (inp.group(1).strip(), out))
|
||||||
|
else:
|
||||||
say(out)
|
say(out)
|
||||||
|
|
2
plugins/flip.py
Normal file → Executable file
2
plugins/flip.py
Normal file → Executable file
|
@ -1,5 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from util import hook
|
from util import hook
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
8
plugins/flirt.py
Normal file → Executable file
8
plugins/flirt.py
Normal file → Executable file
|
@ -43,17 +43,13 @@ flirts = ["I bet your name's Mickey, 'cause you're so fine.",
|
||||||
"Nice legs. What time do they open?",
|
"Nice legs. What time do they open?",
|
||||||
"Your daddy must have been a baker, because you've got a nice set of buns."]
|
"Your daddy must have been a baker, because you've got a nice set of buns."]
|
||||||
|
|
||||||
|
|
||||||
@hook.command(autohelp=False)
|
@hook.command(autohelp=False)
|
||||||
def flirt(inp, nick=None, me=None, input=None):
|
def flirt(inp, nick=None, me=None, input=None):
|
||||||
".flirt <user> -- Make the bot flirt with <user>."
|
".flirt <user> -- Make the bot flirt with <user>."
|
||||||
|
|
||||||
msg = "flirts with " + nick + "... \"" + random.choice(flirts) + "\""
|
msg = "flirts with " + nick + "... \"" + random.choice(flirts) + "\""
|
||||||
if re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()) and inp != "":
|
if re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()) and inp != "":
|
||||||
msg = "flirts with " + inp + "... \"" + random.choice(flirts) + "\""
|
msg = "flirts with " + inp + "... \"" + random.choice(flirts) + "\""
|
||||||
|
|
||||||
if inp == input.conn.nick.lower() or inp == "itself":
|
if inp == input.conn.nick.lower() or inp == "itself":
|
||||||
msg = "flirts with itself!"
|
msg = "flirts with itself... \"" + random.choice(flirts) + "\""
|
||||||
|
|
||||||
me(msg)
|
me(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
2
plugins/fmylife.py
Normal file → Executable file
2
plugins/fmylife.py
Normal file → Executable file
|
@ -14,7 +14,7 @@ base_url = 'http://www.fmylife.com/'
|
||||||
def fml(inp):
|
def fml(inp):
|
||||||
".fml [id] -- Gets a random quote from fmyfife.com. Optionally gets [id]."
|
".fml [id] -- Gets a random quote from fmyfife.com. Optionally gets [id]."
|
||||||
|
|
||||||
inp = inp.replace("#","") # this lets people use .fml #123456
|
inp = inp.replace("#", "")
|
||||||
|
|
||||||
if inp:
|
if inp:
|
||||||
if not inp.isdigit():
|
if not inp.isdigit():
|
||||||
|
|
1
plugins/fortune.py
Normal file → Executable file
1
plugins/fortune.py
Normal file → Executable file
|
@ -63,4 +63,3 @@ def fortune(inp, nick=None, say=None, input=None):
|
||||||
msg = "(@" + inp + ") " + random.choice(fortunes)
|
msg = "(@" + inp + ") " + random.choice(fortunes)
|
||||||
|
|
||||||
say(msg)
|
say(msg)
|
||||||
|
|
||||||
|
|
0
plugins/gcalc.py
Normal file → Executable file
0
plugins/gcalc.py
Normal file → Executable file
2
plugins/geoip.py
Normal file → Executable file
2
plugins/geoip.py
Normal file → Executable file
|
@ -19,6 +19,7 @@ def timezone(ip):
|
||||||
time = time.replace(".00", "")
|
time = time.replace(".00", "")
|
||||||
return int(time)
|
return int(time)
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
@hook.command("location")
|
@hook.command("location")
|
||||||
def geoip(inp, say=None, bot=None):
|
def geoip(inp, say=None, bot=None):
|
||||||
|
@ -37,4 +38,3 @@ def geoip(inp, say = None, bot = None):
|
||||||
else:
|
else:
|
||||||
say("Either that wasn't an IP or I cannot locate it in my database. :(")
|
say("Either that wasn't an IP or I cannot locate it in my database. :(")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
6
plugins/gitio.py
Normal file → Executable file
6
plugins/gitio.py
Normal file → Executable file
|
@ -13,10 +13,12 @@ def gitio(inp):
|
||||||
except:
|
except:
|
||||||
code = None
|
code = None
|
||||||
|
|
||||||
# if the first 8 chars of "url" are not "https://" then append "https://" to the url
|
# if the first 8 chars of "url" are not "https://" then append "https://" to the url, also convert "http://" to "https://"
|
||||||
if url[:8] != "https://":
|
if url[:8] != "https://":
|
||||||
|
if url[:7] != "http://":
|
||||||
url = "https://" + url
|
url = "https://" + url
|
||||||
|
else:
|
||||||
|
url = "https://" + url[7:]
|
||||||
url='url='+str(url)
|
url='url='+str(url)
|
||||||
if code:
|
if code:
|
||||||
url = url + '&code=' + str(code)
|
url = url + '&code=' + str(code)
|
||||||
|
|
0
plugins/google.py
Normal file → Executable file
0
plugins/google.py
Normal file → Executable file
1
plugins/gtime.py
Normal file → Executable file
1
plugins/gtime.py
Normal file → Executable file
|
@ -31,4 +31,3 @@ def clock(inp, say=None):
|
||||||
output = output.decode('utf-8', 'ignore')
|
output = output.decode('utf-8', 'ignore')
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
0
plugins/hash.py
Normal file → Executable file
0
plugins/hash.py
Normal file → Executable file
0
plugins/help.py
Normal file → Executable file
0
plugins/help.py
Normal file → Executable file
0
plugins/imdb.py
Normal file → Executable file
0
plugins/imdb.py
Normal file → Executable file
1
plugins/insult.py
Normal file → Executable file
1
plugins/insult.py
Normal file → Executable file
|
@ -47,4 +47,3 @@ def insult(inp, nick=None, say=None, input=None):
|
||||||
msg = "*stares at " + nick + "*"
|
msg = "*stares at " + nick + "*"
|
||||||
|
|
||||||
say(msg)
|
say(msg)
|
||||||
|
|
||||||
|
|
5
plugins/kill.py
Normal file → Executable file
5
plugins/kill.py
Normal file → Executable file
|
@ -31,11 +31,12 @@ def kill(inp, me = None, nick = None, input=None, notice=None):
|
||||||
return
|
return
|
||||||
|
|
||||||
if inp == input.conn.nick.lower() or inp == "itself":
|
if inp == input.conn.nick.lower() or inp == "itself":
|
||||||
msg = 'kills ' + nick + ' and rakes their corpse (:3)'
|
kill = random.choice(kills)
|
||||||
|
kill = re.sub ('<who>', nick, kill)
|
||||||
|
msg = re.sub ('<body>', random.choice(body), kill)
|
||||||
else:
|
else:
|
||||||
kill = random.choice(kills)
|
kill = random.choice(kills)
|
||||||
kill = re.sub ('<who>', inp, kill)
|
kill = re.sub ('<who>', inp, kill)
|
||||||
msg = re.sub ('<body>', random.choice(body), kill)
|
msg = re.sub ('<body>', random.choice(body), kill)
|
||||||
|
|
||||||
me(msg)
|
me(msg)
|
||||||
|
|
||||||
|
|
118
plugins/lart.py
Executable file
118
plugins/lart.py
Executable file
|
@ -0,0 +1,118 @@
|
||||||
|
from util import hook
|
||||||
|
import re
|
||||||
|
import random
|
||||||
|
|
||||||
|
larts = ["swaps <who>'s shampoo with glue",
|
||||||
|
"installs windows on <who>'s machine",
|
||||||
|
"forces <who> to use perl for 3 weeks",
|
||||||
|
"registers <who>'s name with 50 known spammers",
|
||||||
|
"resizes <who>'s console to 40x24",
|
||||||
|
"takes <who>'s drink",
|
||||||
|
"dispenses <who>'s email address to a few hundred 'bulk mailing services'",
|
||||||
|
"pokes <who> in the eye",
|
||||||
|
"beats <who> senseless with a 50lb Linux manual",
|
||||||
|
"cats /dev/random into <who>'s ear",
|
||||||
|
"signs <who> up for AOL",
|
||||||
|
"enrolls <who> in Visual Basic 101",
|
||||||
|
"sporks <who>",
|
||||||
|
"drops a truckload of support tickets on <who>",
|
||||||
|
"judo chops <who>",
|
||||||
|
"sets <who>'s resolution to 800x600",
|
||||||
|
"formats <who>'s harddrive to fat12",
|
||||||
|
"rm -rf's <who>",
|
||||||
|
"stabs <who>",
|
||||||
|
"steals <who>'s mojo",
|
||||||
|
"strangles <who> with a doohicky mouse cord",
|
||||||
|
"whacks <who> with the cluebat",
|
||||||
|
"sells <who> on EBay",
|
||||||
|
"uses <who> as a biological warfare study",
|
||||||
|
"uses the 'Customer Appreciation Bat' on <who>",
|
||||||
|
"puts <who> in the Total Perspective Vortex",
|
||||||
|
"casts <who> into the fires of Mt. Doom",
|
||||||
|
"gives <who> a melvin",
|
||||||
|
"turns over <who> to Agent Smith to be 'bugged'",
|
||||||
|
"takes away <who>'s internet connection",
|
||||||
|
"pushes <who> past the Shoe Event Horizon",
|
||||||
|
"counts '1, 2, 5... er... 3!' and hurls the Holy Handgrenade Of Antioch at <who>",
|
||||||
|
"puts <who> in a nest of camel spiders",
|
||||||
|
"makes <who> read slashdot at -1",
|
||||||
|
"puts 'alias vim=emacs' in <who>'s /etc/profile",
|
||||||
|
"uninstalls every web browser from <who>'s system",
|
||||||
|
"locks <who> in the Chateau d'If",
|
||||||
|
"signs <who> up for getting hit on the head lessons",
|
||||||
|
"makes <who> try to set up a Lexmark printer",
|
||||||
|
"fills <who>'s eyedrop bottle with lime juice",
|
||||||
|
"casts <who> into the fires of Mt. Doom.",
|
||||||
|
"gives <who> a Flying Dutchman",
|
||||||
|
"rips off <who>'s arm, and uses it to beat them to death",
|
||||||
|
"pierces <who>'s nose with a rusty paper hole puncher",
|
||||||
|
"pokes <who> with a rusty nail",
|
||||||
|
"puts sugar between <who>'s bedsheets",
|
||||||
|
"pours sand into <who>'s breakfast",
|
||||||
|
"mixes epoxy into <who>'s toothpaste",
|
||||||
|
"puts Icy-Hot in <who>'s lube container",
|
||||||
|
"straps <who> to a chair, and plays a endless low bitrate MP3 loop of \"the world's most annoying sound\" from \"Dumb and Dumber\"",
|
||||||
|
"tells Dr. Dre that <who> was talking smack",
|
||||||
|
"forces <who> to use a Commodore 64 for all their word processing",
|
||||||
|
"smacks <who> in the face with a burlap sack full of broken glass",
|
||||||
|
"puts <who> in a room with several heavily armed manic depressives",
|
||||||
|
"makes <who> watch reruns of \"Blue's Clues\"",
|
||||||
|
"puts lye in <who>'s coffee",
|
||||||
|
"tattoos the Windows symbol on <who>'s ass",
|
||||||
|
"lets Borg have his way with <who>",
|
||||||
|
"signs <who> up for line dancing classes at the local senior center",
|
||||||
|
"wakes <who> out of a sound sleep with some brand new nipple piercings",
|
||||||
|
"gives <who> a 2 guage Prince Albert",
|
||||||
|
"forces <who> to eat all their veggies",
|
||||||
|
"covers <who>'s toilet paper with lemon-pepper",
|
||||||
|
"fills <who>'s ketchup bottle with Dave's Insanity sauce",
|
||||||
|
"forces <who> to stare at an incredibly frustrating and seemingly neverending IRC political debate",
|
||||||
|
"knocks two of <who>'s teeth out with a 2x4",
|
||||||
|
"removes debian from <who>'s system",
|
||||||
|
"uses <who>'s iPod for skeet shooting practice",
|
||||||
|
"gives <who>'s phone number to Borg",
|
||||||
|
"posts <who>'s IP, username, and password on 4chan",
|
||||||
|
"forces <who> to use words like 'irregardless' and 'administrate' (thereby sounding like a real dumbass)",
|
||||||
|
"tickles <who> until they wet their pants and pass out",
|
||||||
|
"replaces <who>'s KY with elmer's clear wood glue",
|
||||||
|
"replaces <who>'s TUMS with alka-seltzer tablets",
|
||||||
|
"squeezes habanero pepper juice into <who>'s tub of vaseline",
|
||||||
|
"Forces <who> to learn the Win32 API",
|
||||||
|
"gives <who> an atomic wedgie",
|
||||||
|
"ties <who> to a chair and forces them to listen to 'N Sync at full blast",
|
||||||
|
"forces <who> to use notepad for text editing",
|
||||||
|
"frowns at <who> really really hard",
|
||||||
|
"jabs a hot lighter into <who>'s eye sockets",
|
||||||
|
"forces <who> to browse the web with IE6",
|
||||||
|
"takes <who> out at the knees with a broken pool cue",
|
||||||
|
"forces <who> to listen to emo music",
|
||||||
|
"lets a few creepers into <who>'s house",
|
||||||
|
"signs <who> up for the Iowa State Ferret Legging Championship",
|
||||||
|
"attempts to hotswap <who>'s RAM",
|
||||||
|
"dragon punches <who>",
|
||||||
|
"puts track spikes into <who>'s side",
|
||||||
|
"replaces <who>'s Astroglide with JB Weld",
|
||||||
|
"replaces <who>'s stress pills with rat poison pellets",
|
||||||
|
"replaces <who>s crotch itch cream with Nair",
|
||||||
|
"does the Australian Death Grip on <who>",
|
||||||
|
"dances upon the grave of <who>'s ancestors.",
|
||||||
|
"farts in <who>'s general direction",
|
||||||
|
"flogs <who> with stinging neddle",
|
||||||
|
"assigns all of the permissions tickets on the BeastNode support system to <who>",
|
||||||
|
"hands <who> a poison ivy joint"]
|
||||||
|
|
||||||
|
@hook.command
|
||||||
|
def lart(inp, me=None, nick=None, input=None, notice=None):
|
||||||
|
".lart <user> -- Makes the bot LART <user>."
|
||||||
|
inp = inp.strip()
|
||||||
|
|
||||||
|
if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()):
|
||||||
|
notice("Invalid username!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if inp == input.conn.nick.lower() or inp == "itself":
|
||||||
|
msg = re.sub ('<who>', nick, random.choice(larts))
|
||||||
|
else:
|
||||||
|
msg = re.sub ('<who>', inp, random.choice(larts))
|
||||||
|
|
||||||
|
me(msg)
|
2
plugins/lastfm.py
Normal file → Executable file
2
plugins/lastfm.py
Normal file → Executable file
|
@ -1,3 +1,4 @@
|
||||||
|
# Upgraded with tables/cacheing by ChauffeR of #freebnc on irc.esper.net
|
||||||
from util import hook, http
|
from util import hook, http
|
||||||
|
|
||||||
@hook.command('l')
|
@hook.command('l')
|
||||||
|
@ -67,4 +68,3 @@ def lastfm(inp, nick='', say=None, db=None, bot=None):
|
||||||
ret += " on \x02%s\x0f" % album
|
ret += " on \x02%s\x0f" % album
|
||||||
|
|
||||||
say(ret)
|
say(ret)
|
||||||
|
|
||||||
|
|
0
plugins/log.py
Normal file → Executable file
0
plugins/log.py
Normal file → Executable file
0
plugins/mcping.py
Normal file → Executable file
0
plugins/mcping.py
Normal file → Executable file
1
plugins/mctools.py
Normal file → Executable file
1
plugins/mctools.py
Normal file → Executable file
|
@ -36,4 +36,3 @@ def mcpaid(inp):
|
||||||
return "The account \'" + inp + "\' is a premium Minecraft account!"
|
return "The account \'" + inp + "\' is a premium Minecraft account!"
|
||||||
else:
|
else:
|
||||||
return "The account \'" + inp + "\' is not a premium Minecraft account!"
|
return "The account \'" + inp + "\' is not a premium Minecraft account!"
|
||||||
|
|
||||||
|
|
0
plugins/mem.py
Normal file → Executable file
0
plugins/mem.py
Normal file → Executable file
1
plugins/metacritic.py
Normal file → Executable file
1
plugins/metacritic.py
Normal file → Executable file
|
@ -7,6 +7,7 @@ from util import hook, http
|
||||||
|
|
||||||
|
|
||||||
@hook.command('mc')
|
@hook.command('mc')
|
||||||
|
@hook.command
|
||||||
def metacritic(inp):
|
def metacritic(inp):
|
||||||
".mc [all|movie|tv|album|x360|ps3|pc|ds|wii] <title> -- Gets rating for <title> from metacritic on the specified medium."
|
".mc [all|movie|tv|album|x360|ps3|pc|ds|wii] <title> -- Gets rating for <title> from metacritic on the specified medium."
|
||||||
|
|
||||||
|
|
51
plugins/misc.py
Normal file → Executable file
51
plugins/misc.py
Normal file → Executable file
|
@ -5,26 +5,32 @@ import time
|
||||||
|
|
||||||
from util import hook, http
|
from util import hook, http
|
||||||
|
|
||||||
socket.setdefaulttimeout(10) # global setting
|
socket.setdefaulttimeout(10)
|
||||||
|
|
||||||
|
|
||||||
#autorejoin channels
|
# Auto-join on Invite (Configurable, defaults to True)
|
||||||
#@hook.event('KICK')
|
|
||||||
#def rejoin(paraml, conn=None):
|
|
||||||
# if paraml[1] == conn.nick:
|
|
||||||
# if paraml[0].lower() in conn.channels:
|
|
||||||
# conn.join(paraml[0])
|
|
||||||
|
|
||||||
|
|
||||||
#join channels when invited
|
|
||||||
@hook.event('INVITE')
|
@hook.event('INVITE')
|
||||||
def invite(paraml, conn=None):
|
def invite(paraml, conn=None):
|
||||||
|
invitejoin = conn.conf.get('invitejoin', True)
|
||||||
|
if invitejoin:
|
||||||
conn.join(paraml[-1])
|
conn.join(paraml[-1])
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Rejoin on kick (Configurable, defaults to False)
|
||||||
|
@hook.event('KICK')
|
||||||
|
def rejoin(paraml, conn=None):
|
||||||
|
autorejoin = conn.conf.get('autorejoin', False)
|
||||||
|
if autorejoin:
|
||||||
|
conn.join(paraml[0])
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Identify to NickServ (or other service)
|
||||||
@hook.event('004')
|
@hook.event('004')
|
||||||
def onjoin(paraml, conn=None, bot=None):
|
def onjoin(paraml, conn=None, bot=None):
|
||||||
# identify to services
|
|
||||||
nickserv_password = conn.conf.get('nickserv_password', '')
|
nickserv_password = conn.conf.get('nickserv_password', '')
|
||||||
nickserv_name = conn.conf.get('nickserv_name', 'nickserv')
|
nickserv_name = conn.conf.get('nickserv_name', 'nickserv')
|
||||||
nickserv_command = conn.conf.get('nickserv_command', 'IDENTIFY %s')
|
nickserv_command = conn.conf.get('nickserv_command', 'IDENTIFY %s')
|
||||||
|
@ -34,30 +40,23 @@ def onjoin(paraml, conn=None, bot=None):
|
||||||
conn.msg(nickserv_name, nickserv_command % nickserv_password)
|
conn.msg(nickserv_name, nickserv_command % nickserv_password)
|
||||||
bot.config['censored_strings'].append(nickserv_password)
|
bot.config['censored_strings'].append(nickserv_password)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
# Set bot modes
|
||||||
# set mode on self
|
|
||||||
mode = conn.conf.get('mode')
|
mode = conn.conf.get('mode')
|
||||||
if mode:
|
if mode:
|
||||||
conn.cmd('MODE', [conn.nick, mode])
|
conn.cmd('MODE', [conn.nick, mode])
|
||||||
|
|
||||||
# join channels
|
# Join config-defined channels
|
||||||
for channel in conn.channels:
|
for channel in conn.channels:
|
||||||
conn.join(channel)
|
conn.join(channel)
|
||||||
time.sleep(1) # don't flood JOINs
|
time.sleep(1)
|
||||||
|
|
||||||
# set user-agent
|
# HTTP Useragent
|
||||||
|
http.ua_cloudbot = 'CloudBot - http://git.io/cloudbot'
|
||||||
http.ua_skybot = 'CloudBot'
|
|
||||||
|
|
||||||
# stayalive code
|
|
||||||
|
|
||||||
|
# Stay-alive code
|
||||||
stayalive = conn.conf.get('stayalive')
|
stayalive = conn.conf.get('stayalive')
|
||||||
if stayalive:
|
if stayalive:
|
||||||
|
delay = conn.conf.get('stayalive_delay', 20)
|
||||||
while True:
|
while True:
|
||||||
time.sleep(conn.conf.get('stayalive_delay', 20))
|
time.sleep(delay)
|
||||||
conn.cmd('PING', [conn.nick])
|
conn.cmd('PING', [conn.nick])
|
||||||
|
|
||||||
|
|
||||||
@hook.regex(r'^\x01VERSION\x01$')
|
|
||||||
def version(inp, notice=None):
|
|
||||||
notice('\x01VERSION CloudBot - https://github.com/lukeroge/CloudBot')
|
|
||||||
|
|
0
plugins/mtg.py
Normal file → Executable file
0
plugins/mtg.py
Normal file → Executable file
0
plugins/munge.py
Normal file → Executable file
0
plugins/munge.py
Normal file → Executable file
0
plugins/namegen.py
Normal file → Executable file
0
plugins/namegen.py
Normal file → Executable file
20
plugins/password.py
Normal file → Executable file
20
plugins/password.py
Normal file → Executable file
|
@ -1,9 +1,9 @@
|
||||||
# Password generation code by <TheNoodle>
|
# Password generation code by <TheNoodle>
|
||||||
|
|
||||||
from util import hook
|
from util import hook
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
def gen_password(types):
|
def gen_password(types):
|
||||||
#Password Generator - The Noodle http://bowlofnoodles.net
|
#Password Generator - The Noodle http://bowlofnoodles.net
|
||||||
|
|
||||||
|
@ -50,15 +50,13 @@ def gen_password(types):
|
||||||
password = password + random.choice(okay)
|
password = password + random.choice(okay)
|
||||||
return password
|
return password
|
||||||
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def password(inp, notice=None):
|
def password(inp, notice=None):
|
||||||
".password <length> [types] -- Generates a password of <legenth>. [types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the types, eg. 'numbers symbols'"
|
".password <legenth> [types] -- Generates a password of <legenth> (default 10). [types] can include 'alpha', 'no caps', 'numeric', 'symbols' or any combination of the types, eg. 'numbers symbols'"
|
||||||
if inp == "penis":
|
password = gen_password(inp)
|
||||||
return "error: unable to process request, input too short!"
|
short = "error: input too short"
|
||||||
if inp == "mypenis":
|
penis = ["penis", "mypenis", "dick", "mydick"]
|
||||||
return "error: unable to process request, input too short!"
|
if inp in penis:
|
||||||
if inp == "dick":
|
return short
|
||||||
return "error: unable to process request, input too short!"
|
notice(password)
|
||||||
if inp == "mydick":
|
|
||||||
return "error: unable to process request, input too short!"
|
|
||||||
notice(gen_password(inp))
|
|
||||||
|
|
38
plugins/ping.py
Executable file
38
plugins/ping.py
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
# ping plugin by neersighted
|
||||||
|
from util import hook
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
@hook.command
|
||||||
|
def ping(inp, reply=None):
|
||||||
|
".ping <host> [count] -- Pings <host> [count] times."
|
||||||
|
|
||||||
|
args = inp.split(' ')
|
||||||
|
host = args[0]
|
||||||
|
|
||||||
|
if len(args) > 1:
|
||||||
|
count = args[1]
|
||||||
|
count = int(count)
|
||||||
|
if count > 20:
|
||||||
|
count = 20
|
||||||
|
else:
|
||||||
|
count = 5
|
||||||
|
|
||||||
|
count = str(count)
|
||||||
|
|
||||||
|
host = re.sub(r'([^\s\w\.])+', '', host)
|
||||||
|
|
||||||
|
reply("Attempting to ping %s %s times..." % (host, count))
|
||||||
|
|
||||||
|
pingcmd = subprocess.check_output("ping -c "\
|
||||||
|
+ count + " " + host, shell=True)
|
||||||
|
if 'request timed out' in pingcmd or 'unknown host' in pingcmd:
|
||||||
|
return "error: could not ping host"
|
||||||
|
else:
|
||||||
|
m = re.search(r"rtt min/avg/max/mdev = "\
|
||||||
|
"(\d+.\d+)/(\d+.\d+)/(\d+.\d+)/(\d+.\d+)", pingcmd)
|
||||||
|
return "min: %sms, max: %sms, average: %sms, range: %sms, count: %s"\
|
||||||
|
% (m.group(1), m.group(3), m.group(2), m.group(4), count)
|
||||||
|
|
||||||
|
|
1
plugins/potato.py
Normal file → Executable file
1
plugins/potato.py
Normal file → Executable file
|
@ -18,4 +18,3 @@ def potato(inp, me = None, input=None):
|
||||||
method = random.choice(['bakes', 'fries', 'boils', 'microwaves'])
|
method = random.choice(['bakes', 'fries', 'boils', 'microwaves'])
|
||||||
|
|
||||||
me("%s a %s %s %s potato for %s!" % (method, flavor, size, potato_type, inp))
|
me("%s a %s %s %s potato for %s!" % (method, flavor, size, potato_type, inp))
|
||||||
|
|
||||||
|
|
0
plugins/pycparser/__init__.py
Normal file → Executable file
0
plugins/pycparser/__init__.py
Normal file → Executable file
0
plugins/pycparser/cdecl.py
Normal file → Executable file
0
plugins/pycparser/cdecl.py
Normal file → Executable file
0
plugins/pycparser/lextab.py
Normal file → Executable file
0
plugins/pycparser/lextab.py
Normal file → Executable file
0
plugins/pycparser/pycparser/__init__.py
Normal file → Executable file
0
plugins/pycparser/pycparser/__init__.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_ast.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_ast.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_lexer.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_lexer.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_parser.py
Normal file → Executable file
0
plugins/pycparser/pycparser/c_parser.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/__init__.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/__init__.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/lex.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/lex.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/yacc.py
Normal file → Executable file
0
plugins/pycparser/pycparser/ply/yacc.py
Normal file → Executable file
0
plugins/pycparser/pycparser/plyparser.py
Normal file → Executable file
0
plugins/pycparser/pycparser/plyparser.py
Normal file → Executable file
0
plugins/pycparser/yacctab.py
Normal file → Executable file
0
plugins/pycparser/yacctab.py
Normal file → Executable file
1
plugins/pyexec.py
Normal file → Executable file
1
plugins/pyexec.py
Normal file → Executable file
|
@ -14,7 +14,6 @@ def python(inp):
|
||||||
|
|
||||||
res = http.get("http://eval.appspot.com/eval", statement=inp).splitlines()
|
res = http.get("http://eval.appspot.com/eval", statement=inp).splitlines()
|
||||||
|
|
||||||
|
|
||||||
if len(res) == 0:
|
if len(res) == 0:
|
||||||
return
|
return
|
||||||
res[0] = re_lineends.split(res[0])[0]
|
res[0] = re_lineends.split(res[0])[0]
|
||||||
|
|
0
plugins/quote.py
Normal file → Executable file
0
plugins/quote.py
Normal file → Executable file
1
plugins/repaste.py
Normal file → Executable file
1
plugins/repaste.py
Normal file → Executable file
|
@ -146,7 +146,6 @@ def repaste(inp, input=None, db=None, isManual=True):
|
||||||
if parts[0] == 'list':
|
if parts[0] == 'list':
|
||||||
return " ".join(pasters.keys())
|
return " ".join(pasters.keys())
|
||||||
|
|
||||||
|
|
||||||
paster = paste_gist
|
paster = paste_gist
|
||||||
args = {}
|
args = {}
|
||||||
|
|
||||||
|
|
0
plugins/seen.py
Normal file → Executable file
0
plugins/seen.py
Normal file → Executable file
1
plugins/shorten.py
Normal file → Executable file
1
plugins/shorten.py
Normal file → Executable file
|
@ -9,6 +9,7 @@ from urllib import urlencode
|
||||||
class ShortenError(Exception):
|
class ShortenError(Exception):
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
return repr(self.value)
|
||||||
|
|
||||||
|
|
0
plugins/sieve.py
Normal file → Executable file
0
plugins/sieve.py
Normal file → Executable file
124
plugins/slap.py
Normal file → Executable file
124
plugins/slap.py
Normal file → Executable file
|
@ -2,105 +2,6 @@ from util import hook
|
||||||
import re
|
import re
|
||||||
import random
|
import random
|
||||||
|
|
||||||
larts = ["swaps <who>'s shampoo with glue",
|
|
||||||
"installs windows on <who>'s machine",
|
|
||||||
"forces <who> to use perl for 3 weeks",
|
|
||||||
"registers <who>'s name with 50 known spammers",
|
|
||||||
"resizes <who>'s console to 40x24",
|
|
||||||
"takes <who>'s drink",
|
|
||||||
"dispenses <who>'s email address to a few hundred 'bulk mailing services'",
|
|
||||||
"pokes <who> in the eye",
|
|
||||||
"beats <who> senseless with a 50lb Linux manual",
|
|
||||||
"cats /dev/random into <who>'s ear",
|
|
||||||
"signs <who> up for AOL",
|
|
||||||
"enrolls <who> in Visual Basic 101",
|
|
||||||
"sporks <who>",
|
|
||||||
"drops a truckload of support tickets on <who>",
|
|
||||||
"judo chops <who>",
|
|
||||||
"sets <who>'s resolution to 800x600",
|
|
||||||
"formats <who>'s harddrive to fat12",
|
|
||||||
"rm -rf's <who>",
|
|
||||||
"stabs <who>",
|
|
||||||
"steals <who>'s mojo",
|
|
||||||
"strangles <who> with a doohicky mouse cord",
|
|
||||||
"whacks <who> with the cluebat",
|
|
||||||
"sells <who> on EBay",
|
|
||||||
"uses <who> as a biological warfare study",
|
|
||||||
"uses the 'Customer Appreciation Bat' on <who>",
|
|
||||||
"puts <who> in the Total Perspective Vortex",
|
|
||||||
"casts <who> into the fires of Mt. Doom",
|
|
||||||
"gives <who> a melvin",
|
|
||||||
"turns over <who> to Agent Smith to be 'bugged'",
|
|
||||||
"takes away <who>'s internet connection",
|
|
||||||
"pushes <who> past the Shoe Event Horizon",
|
|
||||||
"counts '1, 2, 5... er... 3!' and hurls the Holy Handgrenade Of Antioch at <who>",
|
|
||||||
"puts <who> in a nest of camel spiders",
|
|
||||||
"makes <who> read slashdot at -1",
|
|
||||||
"puts 'alias vim=emacs' in <who>'s /etc/profile",
|
|
||||||
"uninstalls every web browser from <who>'s system",
|
|
||||||
"locks <who> in the Chateau d'If",
|
|
||||||
"signs <who> up for getting hit on the head lessons",
|
|
||||||
"makes <who> try to set up a Lexmark printer",
|
|
||||||
"fills <who>'s eyedrop bottle with lime juice",
|
|
||||||
"casts <who> into the fires of Mt. Doom.",
|
|
||||||
"gives <who> a Flying Dutchman",
|
|
||||||
"rips off <who>'s arm, and uses it to beat them to death",
|
|
||||||
"pierces <who>'s nose with a rusty paper hole puncher",
|
|
||||||
"pokes <who> with a rusty nail",
|
|
||||||
"puts sugar between <who>'s bedsheets",
|
|
||||||
"pours sand into <who>'s breakfast",
|
|
||||||
"mixes epoxy into <who>'s toothpaste",
|
|
||||||
"puts Icy-Hot in <who>'s lube container",
|
|
||||||
"straps <who> to a chair, and plays a endless low bitrate MP3 loop of \"the world's most annoying sound\" from \"Dumb and Dumber\"",
|
|
||||||
"tells Dr. Dre that <who> was talking smack",
|
|
||||||
"forces <who> to use a Commodore 64 for all their word processing",
|
|
||||||
"smacks <who> in the face with a burlap sack full of broken glass",
|
|
||||||
"puts <who> in a room with several heavily armed manic depressives",
|
|
||||||
"makes <who> watch reruns of \"Blue's Clues\"",
|
|
||||||
"puts lye in <who>'s coffee",
|
|
||||||
"tattoos the Windows symbol on <who>'s ass",
|
|
||||||
"lets Borg have his way with <who>",
|
|
||||||
"signs <who> up for line dancing classes at the local senior center",
|
|
||||||
"wakes <who> out of a sound sleep with some brand new nipple piercings",
|
|
||||||
"gives <who> a 2 guage Prince Albert",
|
|
||||||
"forces <who> to eat all their veggies",
|
|
||||||
"covers <who>'s toilet paper with lemon-pepper",
|
|
||||||
"fills <who>'s ketchup bottle with Dave's Insanity sauce",
|
|
||||||
"forces <who> to stare at an incredibly frustrating and seemingly neverending IRC political debate",
|
|
||||||
"knocks two of <who>'s teeth out with a 2x4",
|
|
||||||
"removes debian from <who>'s system",
|
|
||||||
"uses <who>'s iPod for skeet shooting practice",
|
|
||||||
"gives <who>'s phone number to Borg",
|
|
||||||
"posts <who>'s IP, username, and password on 4chan",
|
|
||||||
"forces <who> to use words like 'irregardless' and 'administrate' (thereby sounding like a real dumbass)",
|
|
||||||
"tickles <who> until they wet their pants and pass out",
|
|
||||||
"replaces <who>'s KY with elmer's clear wood glue",
|
|
||||||
"replaces <who>'s TUMS with alka-seltzer tablets",
|
|
||||||
"squeezes habanero pepper juice into <who>'s tub of vaseline",
|
|
||||||
"Forces <who> to learn the Win32 API",
|
|
||||||
"gives <who> an atomic wedgie",
|
|
||||||
"ties <who> to a chair and forces them to listen to 'N Sync at full blast",
|
|
||||||
"forces <who> to use notepad for text editing",
|
|
||||||
"frowns at <who> really really hard",
|
|
||||||
"jabs a hot lighter into <who>'s eye sockets",
|
|
||||||
"forces <who> to browse the web with IE6",
|
|
||||||
"takes <who> out at the knees with a broken pool cue",
|
|
||||||
"forces <who> to listen to emo music",
|
|
||||||
"lets a few creepers into <who>'s house",
|
|
||||||
"signs <who> up for the Iowa State Ferret Legging Championship",
|
|
||||||
"attempts to hotswap <who>'s RAM",
|
|
||||||
"dragon punches <who>",
|
|
||||||
"puts track spikes into <who>'s side",
|
|
||||||
"replaces <who>'s Astroglide with JB Weld",
|
|
||||||
"replaces <who>'s stress pills with rat poison pellets",
|
|
||||||
"replaces <who>s crotch itch cream with Nair",
|
|
||||||
"does the Australian Death Grip on <who>",
|
|
||||||
"dances upon the grave of <who>'s ancestors.",
|
|
||||||
"farts in <who>'s general direction",
|
|
||||||
"flogs <who> with stinging neddle",
|
|
||||||
"assigns all of the permissions tickets on the BeastNode support system to <who>",
|
|
||||||
"hands <who> a poison ivy joint"]
|
|
||||||
|
|
||||||
slaps = ["slaps <who> with a <item>",
|
slaps = ["slaps <who> with a <item>",
|
||||||
"slaps <who> around a bit with a <item>",
|
"slaps <who> around a bit with a <item>",
|
||||||
"throws a <item> at <who>",
|
"throws a <item> at <who>",
|
||||||
|
@ -118,30 +19,16 @@ items = ["cast iron skillet",
|
||||||
"CRT monitor",
|
"CRT monitor",
|
||||||
"physics textbook",
|
"physics textbook",
|
||||||
"television",
|
"television",
|
||||||
"five tonne truck",
|
"mau5 head",
|
||||||
|
"five tonn truck",
|
||||||
"roll of duct tape",
|
"roll of duct tape",
|
||||||
"book",
|
"book",
|
||||||
"rubber chicken",
|
"rubber chicken",
|
||||||
|
"gold block",
|
||||||
"fire extinguisher",
|
"fire extinguisher",
|
||||||
"heavy rock",
|
"heavy rock",
|
||||||
"chunk of dirt"]
|
"chunk of dirt"]
|
||||||
|
|
||||||
@hook.command
|
|
||||||
def lart(inp, me = None, nick = None, input=None, notice=None):
|
|
||||||
".lart <user> -- Makes the bot LART <user>."
|
|
||||||
inp = inp.strip()
|
|
||||||
|
|
||||||
if not re.match("^[A-Za-z0-9_|.-\]\[]*$", inp.lower()):
|
|
||||||
notice("Invalid username!")
|
|
||||||
return
|
|
||||||
|
|
||||||
if inp == input.conn.nick.lower() or inp == "itself":
|
|
||||||
msg = 'slaps ' + nick + ' in the face!'
|
|
||||||
else:
|
|
||||||
msg = re.sub ('<who>', inp, random.choice(larts))
|
|
||||||
|
|
||||||
me(msg)
|
|
||||||
|
|
||||||
@hook.command
|
@hook.command
|
||||||
def slap(inp, me=None, nick=None, input=None, notice=None):
|
def slap(inp, me=None, nick=None, input=None, notice=None):
|
||||||
".slap <user> -- Makes the bot slap <user>."
|
".slap <user> -- Makes the bot slap <user>."
|
||||||
|
@ -152,11 +39,12 @@ def slap(inp, me = None, nick = None, input=None, notice=None):
|
||||||
return
|
return
|
||||||
|
|
||||||
if inp == input.conn.nick.lower() or inp == "itself":
|
if inp == input.conn.nick.lower() or inp == "itself":
|
||||||
msg = 'slaps ' + nick + ' in the face!'
|
slap = random.choice(slaps)
|
||||||
|
slap = re.sub ('<who>', nick, slap)
|
||||||
|
msg = re.sub ('<item>', random.choice(items), slap)
|
||||||
else:
|
else:
|
||||||
slap = random.choice(slaps)
|
slap = random.choice(slaps)
|
||||||
slap = re.sub ('<who>', inp, slap)
|
slap = re.sub ('<who>', inp, slap)
|
||||||
msg = re.sub ('<item>', random.choice(items), slap)
|
msg = re.sub ('<item>', random.choice(items), slap)
|
||||||
|
|
||||||
me(msg)
|
me(msg)
|
||||||
|
|
||||||
|
|
0
plugins/slogan.py
Normal file → Executable file
0
plugins/slogan.py
Normal file → Executable file
0
plugins/snopes.py
Normal file → Executable file
0
plugins/snopes.py
Normal file → Executable file
1
plugins/spellcheck.py
Normal file → Executable file
1
plugins/spellcheck.py
Normal file → Executable file
|
@ -20,4 +20,3 @@ def spell(inp):
|
||||||
return "That word appears to be valid! (suggestions: " + s_string + ")"
|
return "That word appears to be valid! (suggestions: " + s_string + ")"
|
||||||
else:
|
else:
|
||||||
return "That word appears to be invalid! (suggestions: " + s_string + ")"
|
return "That word appears to be invalid! (suggestions: " + s_string + ")"
|
||||||
|
|
||||||
|
|
0
plugins/stock.py
Normal file → Executable file
0
plugins/stock.py
Normal file → Executable file
1
plugins/tell.py
Normal file → Executable file
1
plugins/tell.py
Normal file → Executable file
|
@ -31,7 +31,6 @@ def tellinput(paraml, input=None, db=None, bot=None):
|
||||||
|
|
||||||
db_init(db)
|
db_init(db)
|
||||||
|
|
||||||
|
|
||||||
tells = get_tells(db, input.nick)
|
tells = get_tells(db, input.nick)
|
||||||
|
|
||||||
if tells:
|
if tells:
|
||||||
|
|
0
plugins/todo.py
Normal file → Executable file
0
plugins/todo.py
Normal file → Executable file
115
plugins/translate.py
Executable file
115
plugins/translate.py
Executable file
|
@ -0,0 +1,115 @@
|
||||||
|
# MyGengo translation plugin by lukeroge and neersighted
|
||||||
|
from util import hook
|
||||||
|
from util import http
|
||||||
|
import re
|
||||||
|
import htmlentitydefs
|
||||||
|
import mygengo
|
||||||
|
|
||||||
|
gengo = mygengo.MyGengo(
|
||||||
|
public_key='PlwtF1CZ2tu27IdX_SXNxTFmfN0j|_-pJ^Rf({O-oLl--r^QM4FygRdt^jusSSDE',
|
||||||
|
private_key='wlXpL=SU[#JpPu[dQaf$v{S3@rg[=95$$TA(k$sb3_6~B_zDKkTbd4#hXxaorIae',
|
||||||
|
sandbox=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def gengo_translate(text, source, target):
|
||||||
|
try:
|
||||||
|
translation = gengo.postTranslationJob(job={
|
||||||
|
'type': 'text',
|
||||||
|
'slug': 'Translating '+source+' to '+target+' with the myGengo API',
|
||||||
|
'body_src': text,
|
||||||
|
'lc_src': source,
|
||||||
|
'lc_tgt': target,
|
||||||
|
'tier': 'machine',
|
||||||
|
})
|
||||||
|
translated = translation['response']['job']['body_tgt']
|
||||||
|
return u"(%s > %s) %s" % (source, target, translated)
|
||||||
|
except mygengo.MyGengoError:
|
||||||
|
return "error: could not translate"
|
||||||
|
|
||||||
|
def match_language(fragment):
|
||||||
|
fragment = fragment.lower()
|
||||||
|
for short, _ in lang_pairs:
|
||||||
|
if fragment in short.lower().split():
|
||||||
|
return short.split()[0]
|
||||||
|
|
||||||
|
for short, full in lang_pairs:
|
||||||
|
if fragment in full.lower():
|
||||||
|
return short.split()[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
@hook.command
|
||||||
|
def translate(inp):
|
||||||
|
".translate <source language> <target language> <sentence> -- Translates <sentence> from <source language> to <target language> using MyGengo."
|
||||||
|
args = inp.split(' ')
|
||||||
|
sl = match_language(args[0])
|
||||||
|
tl = match_language(args[1])
|
||||||
|
txt = unicode(" ".join(args[2:]))
|
||||||
|
if sl and tl:
|
||||||
|
return unicode(gengo_translate(txt, sl, tl))
|
||||||
|
else:
|
||||||
|
return "error: translate could not reliably determine one or both languages"
|
||||||
|
|
||||||
|
languages = 'ja fr de ko ru zh'.split()
|
||||||
|
language_pairs = zip(languages[:-1], languages[1:])
|
||||||
|
lang_pairs = [
|
||||||
|
("no", "Norwegian"),
|
||||||
|
("it", "Italian"),
|
||||||
|
("ht", "Haitian Creole"),
|
||||||
|
("af", "Afrikaans"),
|
||||||
|
("sq", "Albanian"),
|
||||||
|
("ar", "Arabic"),
|
||||||
|
("hy", "Armenian"),
|
||||||
|
("az", "Azerbaijani"),
|
||||||
|
("eu", "Basque"),
|
||||||
|
("be", "Belarusian"),
|
||||||
|
("bg", "Bulgarian"),
|
||||||
|
("ca", "Catalan"),
|
||||||
|
("zh-CN zh", "Chinese"),
|
||||||
|
("hr", "Croatian"),
|
||||||
|
("cs cz", "Czech"),
|
||||||
|
("da dk", "Danish"),
|
||||||
|
("nl", "Dutch"),
|
||||||
|
("en", "English"),
|
||||||
|
("et", "Estonian"),
|
||||||
|
("tl", "Filipino"),
|
||||||
|
("fi", "Finnish"),
|
||||||
|
("fr", "French"),
|
||||||
|
("gl", "Galician"),
|
||||||
|
("ka", "Georgian"),
|
||||||
|
("de", "German"),
|
||||||
|
("el", "Greek"),
|
||||||
|
("ht", "Haitian Creole"),
|
||||||
|
("iw", "Hebrew"),
|
||||||
|
("hi", "Hindi"),
|
||||||
|
("hu", "Hungarian"),
|
||||||
|
("is", "Icelandic"),
|
||||||
|
("id", "Indonesian"),
|
||||||
|
("ga", "Irish"),
|
||||||
|
("it", "Italian"),
|
||||||
|
("ja jp jpn", "Japanese"),
|
||||||
|
("ko", "Korean"),
|
||||||
|
("lv", "Latvian"),
|
||||||
|
("lt", "Lithuanian"),
|
||||||
|
("mk", "Macedonian"),
|
||||||
|
("ms", "Malay"),
|
||||||
|
("mt", "Maltese"),
|
||||||
|
("no", "Norwegian"),
|
||||||
|
("fa", "Persian"),
|
||||||
|
("pl", "Polish"),
|
||||||
|
("pt", "Portuguese"),
|
||||||
|
("ro", "Romanian"),
|
||||||
|
("ru", "Russian"),
|
||||||
|
("sr", "Serbian"),
|
||||||
|
("sk", "Slovak"),
|
||||||
|
("sl", "Slovenian"),
|
||||||
|
("es", "Spanish"),
|
||||||
|
("sw", "Swahili"),
|
||||||
|
("sv", "Swedish"),
|
||||||
|
("th", "Thai"),
|
||||||
|
("tr", "Turkish"),
|
||||||
|
("uk", "Ukrainian"),
|
||||||
|
("ur", "Urdu"),
|
||||||
|
("vi", "Vietnamese"),
|
||||||
|
("cy", "Welsh"),
|
||||||
|
("yi", "Yiddish")
|
||||||
|
]
|
0
plugins/tvdb.py
Normal file → Executable file
0
plugins/tvdb.py
Normal file → Executable file
0
plugins/twitter.py
Normal file → Executable file
0
plugins/twitter.py
Normal file → Executable file
40
plugins/urlparse.py
Executable file
40
plugins/urlparse.py
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
from util import hook, http, urlnorm
|
||||||
|
import urllib
|
||||||
|
from urllib2 import urlopen, Request, HTTPError
|
||||||
|
import re
|
||||||
|
import BeautifulSoup
|
||||||
|
|
||||||
|
ignored_urls = ["http://google.com", "http://youtube.com",
|
||||||
|
"http://pastebin.com", "http://mibpaste.com",
|
||||||
|
"http://fpaste.com", "http://git.io"]
|
||||||
|
|
||||||
|
def parse(match):
|
||||||
|
url = urlnorm.normalize(match.encode('utf-8'))
|
||||||
|
if url not in ignored_urls:
|
||||||
|
url = url.decode('utf-8')
|
||||||
|
try:
|
||||||
|
soup = BeautifulSoup.BeautifulSoup(http.get(url))
|
||||||
|
return soup.title.string
|
||||||
|
except:
|
||||||
|
return "fail"
|
||||||
|
|
||||||
|
@hook.regex(r'([a-zA-Z]://|www\.)?[^ ]+(\.[a-z]+)+')
|
||||||
|
def urlparser(match, say=None):
|
||||||
|
url = urlnorm.normalize(match.group().encode('utf-8'))
|
||||||
|
if url[:7] != "http://":
|
||||||
|
if url[:8] != "https://":
|
||||||
|
url = "http://" + url
|
||||||
|
for x in ignored_urls:
|
||||||
|
if x in url:
|
||||||
|
return
|
||||||
|
title = parse(url)
|
||||||
|
if title == "fail":
|
||||||
|
return
|
||||||
|
title = http.unescape(title)
|
||||||
|
realurl = http.get_url(url)
|
||||||
|
if realurl == url:
|
||||||
|
say("(Link) %s" % title)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
say("(Link) %s [%s]" % (title, realurl))
|
||||||
|
return
|
|
@ -1,65 +0,0 @@
|
||||||
from util import hook, http, urlnorm
|
|
||||||
import urllib
|
|
||||||
from urllib2 import urlopen, Request, HTTPError
|
|
||||||
import re
|
|
||||||
import BeautifulSoup
|
|
||||||
|
|
||||||
ignored_urls = ["http://google.com","http://youtube.com","http://pastebin.com","http://mibpaste.com","http://fpaste.com","beastnode.com"]
|
|
||||||
|
|
||||||
wordDic = {
|
|
||||||
'"': '"',
|
|
||||||
''': '\'',
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'«': '«',
|
|
||||||
'"': '"',
|
|
||||||
''': '\'',
|
|
||||||
'&': '&',
|
|
||||||
'<': '<',
|
|
||||||
'>': '>',
|
|
||||||
'«': '«',
|
|
||||||
'!': '!',
|
|
||||||
'$': '$',
|
|
||||||
' ': ' '}
|
|
||||||
|
|
||||||
def parse(match):
|
|
||||||
url = urlnorm.normalize(match.encode('utf-8'))
|
|
||||||
if url not in ignored_urls:
|
|
||||||
url = url.decode('utf-8')
|
|
||||||
try:
|
|
||||||
soup = BeautifulSoup.BeautifulSoup(http.get(url))
|
|
||||||
return soup.title.string
|
|
||||||
except:
|
|
||||||
return "fail"
|
|
||||||
|
|
||||||
def multiwordReplace(text, wordDic):
|
|
||||||
rc = re.compile('|'.join(map(re.escape, wordDic)))
|
|
||||||
def translate(match):
|
|
||||||
return wordDic[match.group(0)]
|
|
||||||
return rc.sub(translate, text)
|
|
||||||
|
|
||||||
|
|
||||||
#@hook.regex(r'^(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$')
|
|
||||||
#@hook.regex(r'([a-zA-Z]+://|www\.)[^ ]+')
|
|
||||||
def urlparser(match, say = None):
|
|
||||||
print "[debug] URL found"
|
|
||||||
url = urlnorm.normalize(match.group().encode('utf-8'))
|
|
||||||
for x in ignored_urls:
|
|
||||||
if x in url:
|
|
||||||
return
|
|
||||||
title = parse(url)
|
|
||||||
if title == "fail":
|
|
||||||
print "[url] No title found"
|
|
||||||
return
|
|
||||||
title = multiwordReplace(title, wordDic)
|
|
||||||
realurl = http.get_url(url)
|
|
||||||
if realurl == url:
|
|
||||||
say("(Link) %s" % title)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
say("(Link) %s [%s]" % (title, realurl))
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
0
plugins/util/__init__.py
Normal file → Executable file
0
plugins/util/__init__.py
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue