start working on NNTP module
This commit is contained in:
parent
db1e4e9266
commit
6b4a9a2e4a
172
modules/nntp.py
Normal file
172
modules/nntp.py
Normal file
@ -0,0 +1,172 @@
|
||||
"""The NNTP module"""
|
||||
|
||||
# PYTHON STUFFS #######################################################
|
||||
|
||||
import email
|
||||
from email.utils import mktime_tz, parseaddr, parsedate_tz
|
||||
from nntplib import NNTP, decode_header
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from zlib import adler32
|
||||
|
||||
from nemubot import context
|
||||
from nemubot.event import ModuleEvent
|
||||
from nemubot.exception import IMException
|
||||
from nemubot.hooks import hook
|
||||
|
||||
from more import Response
|
||||
|
||||
|
||||
# MODULE CORE #########################################################
|
||||
|
||||
def list_groups(group_pattern="*", **server):
|
||||
with NNTP(**server) as srv:
|
||||
response, l = srv.list(group_pattern)
|
||||
for i in l:
|
||||
yield i.group, srv.description(i.group), i.flag
|
||||
|
||||
def read_group(group, **server):
|
||||
with NNTP(**server) as srv:
|
||||
response, count, first, last, name = srv.group(group)
|
||||
resp, overviews = srv.over((first, last))
|
||||
for art_num, over in reversed(overviews):
|
||||
yield over
|
||||
|
||||
def read_article(msg_id, **server):
|
||||
with NNTP(**server) as srv:
|
||||
response, info = srv.article(msg_id)
|
||||
return email.message_from_bytes(b"\r\n".join(info.lines))
|
||||
|
||||
def whatsnew(date_last_check, group="*", **server):
|
||||
with NNTP(**server) as srv:
|
||||
response, groups = srv.newgroups(date_last_check)
|
||||
for g in groups:
|
||||
yield g
|
||||
|
||||
response, articles = srv.newnews(group, date_last_check)
|
||||
for msg_id in articles:
|
||||
response, info = srv.article(msg_id)
|
||||
yield email.message_from_bytes(b"\r\n".join(info.lines))
|
||||
|
||||
|
||||
def format_article(art, **response_args):
|
||||
art["X-FromName"], art["X-FromEmail"] = parseaddr(art["From"] if "From" in art else "")
|
||||
if art["X-FromName"] == '': art["X-FromName"] = art["X-FromEmail"]
|
||||
|
||||
date = mktime_tz(parsedate_tz(art["Date"]))
|
||||
if date < time.time() - 120:
|
||||
title = "\x0314In \x0F\x03{0:02d}{Newsgroups}\x0F\x0314: on \x0F{Date}\x0314 by \x0F\x03{0:02d}{X-FromName}\x0F \x02{Subject}\x0F"
|
||||
else:
|
||||
title = "\x0314In \x0F\x03{0:02d}{Newsgroups}\x0F\x0314: by \x0F\x03{0:02d}{X-FromName}\x0F \x02{Subject}\x0F"
|
||||
|
||||
return Response(art.get_payload().replace('\n', ' '),
|
||||
title=title.format(adler32(art["Newsgroups"].encode()) & 0xf, adler32(art["X-FromEmail"].encode()) & 0xf, **{h: decode_header(i) for h,i in art.items()}),
|
||||
**response_args)
|
||||
|
||||
def watch(to_server, to_channel, group="*", **server):
|
||||
def newevt(arg):
|
||||
context.add_event(ModuleEvent(call=fini, call_data=arg, interval=42))
|
||||
|
||||
def fini(cnow):
|
||||
newevt(datetime.now())
|
||||
n = 0
|
||||
for art in whatsnew(cnow, group, **server):
|
||||
n += 1
|
||||
if n > 10:
|
||||
continue
|
||||
context.send_response(to_server, format_article(art, channel=to_channel))
|
||||
if n > 10:
|
||||
context.send_response(to_server, Response("... and %s others news" % (n - 10), channel=to_channel))
|
||||
|
||||
newevt(datetime.now())
|
||||
|
||||
|
||||
# MODULE INTERFACE ####################################################
|
||||
|
||||
keywords_server = {
|
||||
"host=HOST": "hostname or IP of the NNTP server",
|
||||
"port=PORT": "port of the NNTP server",
|
||||
"user=USERNAME": "username to use to connect to the server",
|
||||
"password=PASSWORD": "password to use to connect to the server",
|
||||
}
|
||||
|
||||
@hook.command("nntp_groups",
|
||||
help="Show list of existing groups",
|
||||
help_usage={
|
||||
None: "Display all groups",
|
||||
"PATTERN": "Filter on group matching the PATTERN"
|
||||
},
|
||||
keywords=keywords_server)
|
||||
def cmd_groups(msg):
|
||||
if "host" not in msg.kwargs:
|
||||
raise IMException("please give a hostname in keywords")
|
||||
|
||||
return Response(["\x02\x03{0:02d}{1}\x0F: {2}".format(adler32(g[0].encode()) & 0xf, *g) for g in list_groups(msg.args[0] if len(msg.args) > 0 else "*", **msg.kwargs)],
|
||||
channel=msg.channel,
|
||||
title="Matching groups on %s" % msg.kwargs["host"])
|
||||
|
||||
|
||||
@hook.command("nntp_overview",
|
||||
help="Show an overview of articles in given group(s)",
|
||||
help_usage={
|
||||
"GROUP": "Filter on group matching the PATTERN"
|
||||
},
|
||||
keywords=keywords_server)
|
||||
def cmd_overview(msg):
|
||||
if "host" not in msg.kwargs:
|
||||
raise IMException("please give a hostname in keywords")
|
||||
|
||||
if not len(msg.args):
|
||||
raise IMException("which group would you overview?")
|
||||
|
||||
for g in msg.args:
|
||||
arts = []
|
||||
for grp in read_group(g, **msg.kwargs):
|
||||
grp["X-FromName"], grp["X-FromEmail"] = parseaddr(grp["from"] if "from" in grp else "")
|
||||
if grp["X-FromName"] == '': grp["X-FromName"] = grp["X-FromEmail"]
|
||||
|
||||
arts.append("On {date}, from \x03{0:02d}{X-FromName}\x0F \x02{subject}\x0F: \x0314{message-id}\x0F".format(adler32(grp["X-FromEmail"].encode()) & 0xf, **{h: decode_header(i) for h,i in grp.items()}))
|
||||
|
||||
if len(arts):
|
||||
yield Response(arts,
|
||||
channel=msg.channel,
|
||||
title="In \x03{0:02d}{1}\x0F".format(adler32(g[0].encode()) & 0xf, g))
|
||||
|
||||
|
||||
@hook.command("nntp_read",
|
||||
help="Read an article from a server",
|
||||
help_usage={
|
||||
"MSG_ID": "Read the given message"
|
||||
},
|
||||
keywords=keywords_server)
|
||||
def cmd_read(msg):
|
||||
if "host" not in msg.kwargs:
|
||||
raise IMException("please give a hostname in keywords")
|
||||
|
||||
for msgid in msg.args:
|
||||
if not re.match("<.*>", msgid):
|
||||
msgid = "<" + msgid + ">"
|
||||
art = read_article(msgid, **msg.kwargs)
|
||||
yield format_article(art, channel=msg.channel)
|
||||
|
||||
|
||||
@hook.command("nntp_watch",
|
||||
help="Launch an event looking for new groups and articles on a server",
|
||||
help_usage={
|
||||
None: "Watch all groups",
|
||||
"PATTERN": "Limit the watch on group matching this PATTERN"
|
||||
},
|
||||
keywords=keywords_server)
|
||||
def cmd_watch(msg):
|
||||
if "host" not in msg.kwargs:
|
||||
raise IMException("please give a hostname in keywords")
|
||||
|
||||
print(type(msg))
|
||||
|
||||
if not msg.frm_owner:
|
||||
raise IMException("sorry, this command is currently limited to the owner")
|
||||
|
||||
watch(msg.server, msg.channel, msg.args[0] if len(msg.args) > 0 else "*", **msg.kwargs)
|
||||
|
||||
return Response("Ok ok, I watch this newsgroup!", channel=msg.channel)
|
Loading…
x
Reference in New Issue
Block a user