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