From 096912c08aa6b4fcfe5e826936887bc7a7577e97 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 08:21:43 +0530 Subject: [PATCH 1/7] Added Features - Last seen - Last few lines --- ekan0ra.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index 0d879fb..ac2fa3a 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -8,7 +8,7 @@ import time, sys, os import datetime import config as conf - +from collections import deque import fpaste commands = [ @@ -22,6 +22,8 @@ ('startclass', 'start logging the class'), ('endclass', 'ends logging the class'), ('pingall:[message]', 'pings the message to all'), + ('lastwords:[nick]','show last 10 lines of the user'), + ('lastseen:[nick]','shows last seen datetime'), ('help', 'list all the commands'), ] @@ -52,11 +54,13 @@ class LogBot(irc.IRCClient): nickname = conf.botnick - def __init__(self, channel): + def __init__(self, channel): self.chn = '#'+channel self.channel_admin = conf.channel_admin self.qs_queue = [] self.logger = None + self.lastseen = {} + self.lastspoken = {} def clearqueue(self): self.qs_queue = [] @@ -72,18 +76,17 @@ def startlogging(self, user, msg): self.logger = MessageLogger(open(self.filename, "a")) self.logger.log("[## Class Started at %s ##]" % - time.asctime(time.localtime(time.time()))) + time.asctime(time.localtime(time.time()))) user = user.split('!', 1)[0] self.logger.log("<%s> %s" % (user, msg)) - self.islogging = True + self.islogging = False def stoplogging(self, channel): if not self.logger: return - self.logger.log("[## Class Ended at %s ##]" % - time.asctime(time.localtime(time.time()))) + self.logger.log("[## Class Ended at %s ##]" % time.asctime(time.localtime(time.time()))) self.logger.close() - #self.upload_logs(channel) + self.upload_logs(channel) self.islogging = False def connectionLost(self, reason): @@ -103,6 +106,7 @@ def pingall(self, nicklist): def privmsg(self, user, channel, msg): """This will get called when the bot receives a message.""" user = user.split('!', 1)[0] + self.updateLastSeen(user) if self.islogging: user = user.split('!', 1)[0] self.logger.log("<%s> %s" % (user, msg)) @@ -155,6 +159,21 @@ def privmsg(self, user, channel, msg): for command, help_txt in commands: self.msg(user, help_template.format(command=command, help_text=help_txt)) + if msg.startswith('lastwords'): + nick = msg.split(':')[-1] + if nick in self.lastspoken: + for i in self.lastspoken[nick]: + self.msg(channel,i) + + if msg.startswith('lastseen'): + nick = msg.split(':')[-1] + self.names(channel).addCallback(self.activityTracker,nick=nick,channel=channel) + + if user in self.lastspoken: + self.lastspoken[user].append(msg) + else: + self.lastspoken[user] = deque(maxlen=10) + self.lastspoken[user].append(msg) if channel == self.nickname: @@ -177,6 +196,7 @@ def action(self, user, channel, msg): user = user.split('!', 1)[0] if self.islogging: self.logger.log("* %s %s" % (user, msg)) + pass # irc callbacks @@ -186,7 +206,32 @@ def irc_NICK(self, prefix, params): new_nick = params[0] if self.islogging: self.logger.log("%s is now known as %s" % (old_nick, new_nick)) + pass + + def userLeft(self,user,channel): + self.updateLastSeen(user) + + def userQuit(self,user,quitMessage): + self.updateLastSeen(user) + def uesrJoined(self,nick,channel): + self.updateLastSeen(user) + + def updateLastSeen(self,user): + self.lastseen[user]=datetime.datetime.now().strftime('%c') + + def activityTracker(self,nicklist,nick,channel): + if nick in nicklist: + if self.lastseen.get(nick): + self.msg(channel, "%s is online now, last activity at %s" % (nick,self.lastseen[nick])) + else: + self.msg(channel, "%s is online now, last activity not known" % (nick)) + + else: + if nick in self.lastseen: + self.msg(channel,"last seen activity was on %s"%self.lastseen[nick]) + else: + self.msg(channel,"no data found") # For fun, override the method that determines how a nickname is changed on # collisions. The default method appends an underscore. From 82bdbfef9dca5e81806edc9b798ba94ab7895e40 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 08:58:27 +0530 Subject: [PATCH 2/7] Added Feature - Added spell correction command --- ekan0ra.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index ac2fa3a..a41382e 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -154,6 +154,14 @@ def privmsg(self, user, channel, msg): self.channel_admin = filter(lambda x: x != name, self.channel_admin) except Exception, err: print err + + if msg.startswith('s\\'): + wordlist = msg.split('\\')[1::] + line = self.lastspoken[user][-1] + for target,replace in zip(wordlist[0::2],wordlist[1::2]): + line = line.replace(target,replace) + statement = "what {user} meant is , {line}".format(user=user,line=line) + self.msg(channel,statement) if msg == 'help': for command, help_txt in commands: @@ -162,8 +170,8 @@ def privmsg(self, user, channel, msg): if msg.startswith('lastwords'): nick = msg.split(':')[-1] if nick in self.lastspoken: - for i in self.lastspoken[nick]: - self.msg(channel,i) + for line in self.lastspoken[nick]: + self.msg(channel,line) if msg.startswith('lastseen'): nick = msg.split(':')[-1] From 31407e83084dabf8f920ecc53086b79ddfc42d37 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 09:02:47 +0530 Subject: [PATCH 3/7] code cleaning --- ekan0ra.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index a41382e..f996aba 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -204,7 +204,7 @@ def action(self, user, channel, msg): user = user.split('!', 1)[0] if self.islogging: self.logger.log("* %s %s" % (user, msg)) - pass + # irc callbacks @@ -214,7 +214,7 @@ def irc_NICK(self, prefix, params): new_nick = params[0] if self.islogging: self.logger.log("%s is now known as %s" % (old_nick, new_nick)) - pass + def userLeft(self,user,channel): self.updateLastSeen(user) From 2d739f2353136d628256c56dbec8d22c389bee39 Mon Sep 17 00:00:00 2001 From: CuriousLearner Date: Tue, 21 Jun 2016 23:50:16 +0530 Subject: [PATCH 4/7] PEP8 Formatted --- config.py | 6 +- ekan0ra.py | 36 ++-- fpaste.py | 605 +++++++++++++++++++++++++++++------------------------ 3 files changed, 356 insertions(+), 291 deletions(-) diff --git a/config.py b/config.py index 52d618c..d166a0c 100644 --- a/config.py +++ b/config.py @@ -1,2 +1,4 @@ -botnick = 'batul' # The nick of the bot. -channel_admin = ['kushal','sayan','mbuf','rtnpro','chandankumar','praveenkumar'] # List of IRC nicks as masters. +botnick = 'batul' # The nick of the bot. +# List of IRC nicks as masters. +channel_admin = ['kushal', 'sayan', 'mbuf', + 'rtnpro', 'chandankumar', 'praveenkumar'] diff --git a/ekan0ra.py b/ekan0ra.py index 5aa2d20..5e827cb 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -5,7 +5,9 @@ from twisted.internet import defer # system imports -import time, sys, os +import time +import sys +import os import datetime import config as conf import json @@ -31,11 +33,13 @@ {command} - {help_text} """ + class MessageLogger(object): """ An independent logger class (because separation of application and protocol logic is a good thing). """ + def __init__(self, file): self.file = file @@ -54,8 +58,8 @@ class LogBot(irc.IRCClient): nickname = conf.botnick - def __init__(self, channel): - self.chn = '#'+channel + def __init__(self, channel): + self.chn = '#' + channel self.channel_admin = conf.channel_admin self.qs_queue = [] self.links_reload() @@ -71,11 +75,11 @@ def connectionMade(self): def startlogging(self, user, msg): now = datetime.datetime.now() - self.filename = "Logs-%s.txt"%now.strftime("%Y-%m-%d-%H-%M") + self.filename = "Logs-%s.txt" % now.strftime("%Y-%m-%d-%H-%M") self.logger = MessageLogger(open(self.filename, "a")) self.logger.log("[## Class Started at %s ##]" % - time.asctime(time.localtime(time.time()))) + time.asctime(time.localtime(time.time()))) user = user.split('!', 1)[0] self.logger.log("<%s> %s" % (user, msg)) self.islogging = True @@ -86,7 +90,7 @@ def stoplogging(self, channel): self.logger.log("[## Class Ended at %s ##]" % time.asctime(time.localtime(time.time()))) self.logger.close() - #self.upload_logs(channel) + # self.upload_logs(channel) self.islogging = False def connectionLost(self, reason): @@ -99,7 +103,8 @@ def signedOn(self): def pingall(self, nicklist): """Called to ping all with a message""" - msg = ', '.join([nick for nick in nicklist if nick != self.nickname and nick not in self.channel_admin]) + msg = ', '.join([nick for nick in nicklist if nick != + self.nickname and nick not in self.channel_admin]) self.msg(self.chn, msg) self.msg(self.chn, self.pingmsg.lstrip()) @@ -118,10 +123,11 @@ def privmsg(self, user, channel, msg): # Check to see if they're sending me a private message user_cond = user in self.channel_admin - if msg == '!' and self.islogging: + if msg == '!' and self.islogging: self.qs_queue.append(user) if msg == '!' and not self.islogging: - self.msg(self.chn, '%s no session is going on, feel free to ask a question. You do not have to type !' % user) + self.msg( + self.chn, '%s no session is going on, feel free to ask a question. You do not have to type !' % user) return if msg == 'givemelogs': import sys @@ -139,24 +145,27 @@ def privmsg(self, user, channel, msg): name = self.qs_queue.pop(0) msg = "%s please ask your question." % name if len(self.qs_queue) > 0: - msg = "%s. %s you are next. Get ready with your question." % (msg, self.qs_queue[0]) + msg = "%s. %s you are next. Get ready with your question." % ( + msg, self.qs_queue[0]) self.msg(self.chn, msg) else: self.msg(self.chn, "No one is in queue.") if msg == 'masters' and user_cond: - self.msg(self.chn, "My current masters are: %s" % ",".join(self.channel_admin)) + self.msg(self.chn, "My current masters are: %s" % + ",".join(self.channel_admin)) if msg.startswith('add:') and user_cond: try: name = msg.split()[1] print name self.channel_admin.append(name) - self.msg(self.chn,'%s is a master now.' % name) + self.msg(self.chn, '%s is a master now.' % name) except Exception, err: print err if msg.startswith('rm:') and user_cond: try: name = msg.split()[1] - self.channel_admin = filter(lambda x: x != name, self.channel_admin) + self.channel_admin = filter( + lambda x: x != name, self.channel_admin) except Exception, err: print err @@ -199,7 +208,6 @@ def irc_NICK(self, prefix, params): if self.islogging: self.logger.log("%s is now known as %s" % (old_nick, new_nick)) - # For fun, override the method that determines how a nickname is changed on # collisions. The default method appends an underscore. def alterCollidedNick(self, nickname): diff --git a/fpaste.py b/fpaste.py index 1abc9a9..7de23b5 100644 --- a/fpaste.py +++ b/fpaste.py @@ -22,10 +22,16 @@ #FPASTE_URL = 'http://fpaste.org/' FPASTE_URL = 'http://paste.fedoraproject.org/' -import os, sys, urllib, urllib2, subprocess, json +import os +import sys +import urllib +import urllib2 +import subprocess +import json from optparse import OptionParser, OptionGroup, SUPPRESS_HELP -def is_text(text, maxCheck = 100, pctPrintable = 0.75): + +def is_text(text, maxCheck=100, pctPrintable=0.75): '''returns true if maxCheck evenly distributed chars in text are >= pctPrintable% text chars''' # e.g.: /bin/* ranges between 19% and 42% printable from string import printable @@ -33,7 +39,7 @@ def is_text(text, maxCheck = 100, pctPrintable = 0.75): if nchars == 0: return False ncheck = min(nchars, maxCheck) - inc = float(nchars)/ncheck + inc = float(nchars) / ncheck i = 0.0 nprintable = 0 while i < nchars: @@ -44,7 +50,7 @@ def is_text(text, maxCheck = 100, pctPrintable = 0.75): return (pct >= pctPrintable) -def confirm(prompt = "OK?"): +def confirm(prompt="OK?"): '''prompt user for yes/no input and return True or False''' prompt += " [y/N]: " try: @@ -64,16 +70,17 @@ def confirm(prompt = "OK?"): else: return False + def get_shortened_url(long_url, password): '''Get shortened URL from paste data''' # NOTE: this uses password, not paste_password if password: - params = urllib.urlencode({'mode':'json', 'password': password}) + params = urllib.urlencode({'mode': 'json', 'password': password}) else: params = 'mode=json' - req = urllib2.Request(url=long_url+'/', data=params) + req = urllib2.Request(url=long_url + '/', data=params) try: f = urllib2.urlopen(req) except urllib2.URLError: @@ -83,23 +90,24 @@ def get_shortened_url(long_url, password): # Iterating over each line is a bad idea. It'll break everytime it # encounters a "short_url" string. try: - result = json.loads('{' + f.readlines()[-3] +'}')['short_url'] + result = json.loads('{' + f.readlines()[-3] + '}')['short_url'] except ValueError, e: return False return result + def paste(text, options): '''send text to fpaste.org and return the URL''' import re if not text: print >> sys.stderr, "No text to send." - return [False,False] + return [False, False] # if sent data exceeds maxlength, server dies without error returned, so, we'll truncate the input here, # until the server decides to truncate instead of die author = options.nick if len(author) > 50: - author = author[0:50-3] + "..." + author = author[0:50 - 3] + "..." params = urllib.urlencode({'paste_lang': options.lang, 'paste_data': text, 'paste_private': options.make_private, @@ -107,17 +115,21 @@ def paste(text, options): 'paste_password': options.password, 'paste_user': author, 'api_submit': 'true', 'mode': 'json'}) - pasteSizeKiB = len(params)/1024.0 + pasteSizeKiB = len(params) / 1024.0 - if pasteSizeKiB >= 512: # 512KiB appears to be the current hard limit (20110404); old limit was 16MiB - print >> sys.stderr, "WARNING: your paste size (%.1fKiB) is very large and may be rejected by the server. A pastebin is NOT a file hosting service!" % (pasteSizeKiB) + # 512KiB appears to be the current hard limit (20110404); old limit was + # 16MiB + if pasteSizeKiB >= 512: + print >> sys.stderr, "WARNING: your paste size (%.1fKiB) is very large and may be rejected by the server. A pastebin is NOT a file hosting service!" % ( + pasteSizeKiB) # verify that it's most likely *non-binary* data being sent. if not is_text(text): print >> sys.stderr, "WARNING: your paste looks a lot like binary data instead of text." if not confirm("Send binary data anyway?"): - return [False,False] + return [False, False] - req = urllib2.Request(url=options.url, data=params, headers={'User-agent': USER_AGENT}) + req = urllib2.Request(url=options.url, data=params, + headers={'User-agent': USER_AGENT}) if options.proxy: if options.debug: print >> sys.stderr, "Using proxy: %s" % options.proxy @@ -187,40 +199,49 @@ def paste(text, options): # return False -def sysinfo(show_stderr = False, show_successful_cmds = True, show_failed_cmds = True): +def sysinfo(show_stderr=False, show_successful_cmds=True, show_failed_cmds=True): '''returns commonly requested (and some fedora-specific) system info''' # 'ps' output below has been anonymized: -n for uid vs username, and -c for short processname # cmd name, command, command2 fallback, command3 fallback, ... cmdlist = [ - ('OS Release', '''lsb_release -ds''', '''cat /etc/*-release | uniq''', 'cat /etc/issue', 'cat /etc/motd'), + ('OS Release', '''lsb_release -ds''', + '''cat /etc/*-release | uniq''', 'cat /etc/issue', 'cat /etc/motd'), ('Kernel', '''uname -r ; cat /proc/cmdline'''), ('Desktop(s) Running', '''ps -eo comm= | grep -E '(gnome-session|startkde|startactive|xfce.?-session|fluxbox|blackbox|hackedbox|ratpoison|enlightenment|icewm-session|od-session|wmaker|wmx|openbox-lxde|openbox-gnome-session|openbox-kde-session|mwm|e16|fvwm|xmonad|sugar-session|mate-session|lxqt-session|cinnamon)' '''), ('Desktop(s) Installed', '''ls -m /usr/share/xsessions/ | sed 's/\.desktop//g' '''), - ('SELinux Status', '''sestatus''', '''/usr/sbin/sestatus''', '''getenforce''', '''grep -v '^#' /etc/sysconfig/selinux'''), - ('SELinux Error Count', '''selinuxenabled && journalctl --since yesterday |grep avc: |grep -Eo "comm=\"[^ ]+" |sort |uniq -c |sort -rn'''), - ('CPU Model', '''grep 'model name' /proc/cpuinfo | awk -F: '{print $2}' | uniq -c | sed -re 's/^ +//' ''', '''grep 'model name' /proc/cpuinfo'''), + ('SELinux Status', '''sestatus''', '''/usr/sbin/sestatus''', + '''getenforce''', '''grep -v '^#' /etc/sysconfig/selinux'''), + ('SELinux Error Count', + '''selinuxenabled && journalctl --since yesterday |grep avc: |grep -Eo "comm=\"[^ ]+" |sort |uniq -c |sort -rn'''), + ('CPU Model', + '''grep 'model name' /proc/cpuinfo | awk -F: '{print $2}' | uniq -c | sed -re 's/^ +//' ''', '''grep 'model name' /proc/cpuinfo'''), ('64-bit Support', '''grep -q ' lm ' /proc/cpuinfo && echo Yes || echo No'''), - ('Hardware Virtualization Support', '''grep -Eq '(vmx|svm)' /proc/cpuinfo && echo Yes || echo No'''), + ('Hardware Virtualization Support', + '''grep -Eq '(vmx|svm)' /proc/cpuinfo && echo Yes || echo No'''), ('Load average', '''uptime'''), ('Memory usage', '''free -m''', 'free'), #('Top', '''top -n1 -b | head -15'''), - ('Top 5 CPU hogs', '''ps axuScnh | awk '$2!=''' + str(os.getpid()) + '''' | sort -rnk3 | head -5'''), + ('Top 5 CPU hogs', '''ps axuScnh | awk '$2!=''' + \ + str(os.getpid()) + '''' | sort -rnk3 | head -5'''), ('Top 5 Memory hogs', '''ps axuScnh | sort -rnk4 | head -5'''), ('Disk space usage', '''df -hT''', 'df -h', 'df'), ('Block devices', '''blkid''', '''/sbin/blkid'''), ('PCI devices', '''lspci''', '''/sbin/lspci'''), ('USB devices', '''lsusb''', '''/sbin/lsusb'''), - ('DRM Information', '''journalctl -k -b | grep -o 'kernel:.*drm.*$' | cut -d ' ' -f 2- '''), + ('DRM Information', + '''journalctl -k -b | grep -o 'kernel:.*drm.*$' | cut -d ' ' -f 2- '''), ('Xorg modules', '''grep LoadModule /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log | cut -d \\" -f 2 | xargs'''), ('GL Support', '''glxinfo | grep -E "OpenGL version|OpenGL renderer"'''), - ('Xorg errors', '''grep '^\[.*(EE)' /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log | cut -d ':' -f 2- '''), + ('Xorg errors', + '''grep '^\[.*(EE)' /var/log/Xorg.0.log ~/.local/share/xorg/Xorg.0.log | cut -d ':' -f 2- '''), ('Kernel buffer tail', '''dmesg | tail'''), ('Last few reboots', '''last -x -n10 reboot runlevel'''), - ('DNF Repositories', '''dnf -C repolist''', '''ls -l /etc/yum.repos.d''', '''grep -v '^#' /etc/yum.conf'''), + ('DNF Repositories', '''dnf -C repolist''', + '''ls -l /etc/yum.repos.d''', '''grep -v '^#' /etc/yum.conf'''), ('DNF Extras', '''dnf -C list extras'''), ('Last 20 packages installed', '''rpm -qa --nodigest --nosignature --last | head -20''')] - #('Installed packages', '''rpm -qa --nodigest --nosignature | sort''', '''dpkg -l''') ] + #('Installed packages', '''rpm -qa --nodigest --nosignature | sort''', '''dpkg -l''') ] si = [] print >> sys.stderr, "Gathering system info", @@ -228,26 +249,28 @@ def sysinfo(show_stderr = False, show_successful_cmds = True, show_failed_cmds = cmdname = cmds[0] cmd = "" for cmd in cmds[1:]: - sys.stderr.write('.') # simple progress feedback - p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sys.stderr.write('.') # simple progress feedback + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = p.communicate() if p.returncode == 0 and out: break else: if show_stderr: - print >> sys.stderr, "sysinfo Error: the cmd \"%s\" returned %d with stderr: %s" % (cmd, p.returncode, err) + print >> sys.stderr, "sysinfo Error: the cmd \"%s\" returned %d with stderr: %s" % ( + cmd, p.returncode, err) print >> sys.stderr, "Trying next fallback cmd..." if out: if show_successful_cmds: - si.append( ('%s (%s)' % (cmdname, cmd), out) ) + si.append(('%s (%s)' % (cmdname, cmd), out)) else: - si.append( ('%s' % cmdname, out) ) + si.append(('%s' % cmdname, out)) else: if show_failed_cmds: - si.append( ('%s (failed: "%s")' % (cmdname, '" AND "'.join(cmds[1:])), out) ) + si.append(('%s (failed: "%s")' % + (cmdname, '" AND "'.join(cmds[1:])), out)) else: - si.append( ('%s' % cmdname, out) ) - + si.append(('%s' % cmdname, out)) # return in readable indented format sistr = "=== fpaste %s System Information (fpaste --sysinfo) ===\n" % VERSION @@ -270,233 +293,235 @@ def generate_man_page(): def summarize_text(text): # use beginning/middle/end content snippets as a description summary. 120 char limit # "36chars ... 36chars ... 36chars" == 118 chars - # TODO: nuking whitespace in huge text files might be expensive; optimize for b/m/e segments only + # TODO: nuking whitespace in huge text files might be expensive; optimize + # for b/m/e segments only sniplen = 36 seplen = len(" ... ") tsum = "" text = " ".join(text.split()) # nuke whitespace tlen = len(text) - if tlen < sniplen+seplen: + if tlen < sniplen + seplen: tsum += text - if tlen >= sniplen+seplen: + if tlen >= sniplen + seplen: tsum += text[0:sniplen] + " ..." - if tlen >= (sniplen*2)+seplen: - tsum += " " + text[tlen/2-(sniplen/2):(tlen/2)+(sniplen/2)] + " ..." - if tlen >= (sniplen*3)+(seplen*2): + if tlen >= (sniplen * 2) + seplen: + tsum += " " + text[tlen / 2 - + (sniplen / 2):(tlen / 2) + (sniplen / 2)] + " ..." + if tlen >= (sniplen * 3) + (seplen * 2): tsum += " " + text[-sniplen:] #print >> sys.stderr, str(len(tsum)) + ": " + tsum return tsum - def main(): - validExpiresOpts = [ '1800', '21600', '86400', '604800', '2592000'] - validSyntaxOpts = [ "cpp", - "diff", - "gdb", - "javascript", - "text", - "perl", - "php", - "python", - "ruby", - "xml", - "abap", - "6502acme", - "actionscript", - "actionscript3", - "ada", - "algol68", - "apache", - "applescript", - "apt_sources", - "asm", - "asp", - "autoconf", - "autohotkey", - "autoit", - "avisynth", - "awk", - "bash", - "basic4gl", - "bf", - "bibtex", - "blitzbasic", - "bnf", - "boo", - "c", - "c_loadrunner", - "c_mac", - "caddcl", - "cadlisp", - "cfdg", - "cfm", - "chaiscript", - "cil", - "clojure", - "cmake", - "cobol", - "cpp", - "cpp-qt", - "csharp", - "css", - "cuesheet", - "d", - "dcs", - "delphi", - "diff", - "div", - "dos", - "dot", - "e", - "ecmascript", - "eiffel", - "email", - "epc", - "erlang", - "f1", - "falcon", - "fo", - "fortran", - "freebasic", - "fsharp", - "4cs", - "gambas", - "gdb", - "genero", - "genie", - "gettext", - "glsl", - "gml", - "gnuplot", - "go", - "groovy", - "gwbasic", - "haskell", - "hicest", - "68000devpac", - "hq9plus", - "html4strict", - "icon", - "idl", - "ini", - "inno", - "intercal", - "io", - "j", - "java", - "java5", - "javascript", - "jquery", - "6502kickass", - "kixtart", - "klonec", - "klonecpp", - "latex", - "lb", - "lisp", - "locobasic", - "logtalk", - "lolcode", - "lotusformulas", - "lotusscript", - "lscript", - "lsl2", - "lua", - "m68k", - "magiksf", - "make", - "mapbasic", - "matlab", - "mirc", - "mmix", - "modula2", - "modula3", - "mpasm", - "mxml", - "mysql", - "newlisp", - "nsis", - "oberon2", - "objc", - "objeck", - "ocaml", - "ocaml-brief" , - "oobas", - "oracle11", - "oracle8", - "oxygene", - "oz", - "pascal", - "pcre", - "per", - "perl", - "perl6", - "pf", - "php", - "php-brief", - "pic16", - "pike", - "pixelbender", - "plsql", - "postgresql", - "povray", - "powerbuilder", - "powershell", - "progress", - "prolog", - "properties", - "providex", - "purebasic", - "python", - "q", - "qbasic", - "rails", - "rebol", - "reg", - "robots", - "rpmspec", - "rsplus", - "ruby", - "sas", - "scala", - "scheme", - "scilab", - "sdlbasic", - "smalltalk", - "smarty", - "sql", - "systemverilog", - "6502tasm", - "tcl", - "teraterm", - "text", - "thinbasic", - "tsql", - "typoscript", - "unicon", - "vala", - "vb", - "vbnet", - "verilog", - "vhdl", - "vim", - "visualfoxpro", - "visualprolog", - "whitespace", - "whois", - "winbatch", - "xbasic", - "xml", - "xorg_conf", - "xpp", - "z80", - "zxbasic" ] - validClipboardSelectionOpts = [ 'primary', 'secondary', 'clipboard' ] - validPrivateOpts = [ 'yes', 'no'] - ext2lang_map = { 'sh':'bash', 'bash':'bash', 'bat':'bat', 'c':'c', 'h':'c', 'cpp':'cpp', 'css':'css', 'html':'html4strict', 'htm':'html4strict', 'ini':'ini', 'java':'java', 'js':'javascript', 'jsp':'java', 'pl':'perl', 'php':'php', 'php3':'php', 'py':'python', 'rb':'ruby', 'rhtml':'html4strict', 'sql':'sql', 'sqlite':'sql', 'tcl':'tcl', 'vim':'vim', 'xml':'xml' } + validExpiresOpts = ['1800', '21600', '86400', '604800', '2592000'] + validSyntaxOpts = ["cpp", + "diff", + "gdb", + "javascript", + "text", + "perl", + "php", + "python", + "ruby", + "xml", + "abap", + "6502acme", + "actionscript", + "actionscript3", + "ada", + "algol68", + "apache", + "applescript", + "apt_sources", + "asm", + "asp", + "autoconf", + "autohotkey", + "autoit", + "avisynth", + "awk", + "bash", + "basic4gl", + "bf", + "bibtex", + "blitzbasic", + "bnf", + "boo", + "c", + "c_loadrunner", + "c_mac", + "caddcl", + "cadlisp", + "cfdg", + "cfm", + "chaiscript", + "cil", + "clojure", + "cmake", + "cobol", + "cpp", + "cpp-qt", + "csharp", + "css", + "cuesheet", + "d", + "dcs", + "delphi", + "diff", + "div", + "dos", + "dot", + "e", + "ecmascript", + "eiffel", + "email", + "epc", + "erlang", + "f1", + "falcon", + "fo", + "fortran", + "freebasic", + "fsharp", + "4cs", + "gambas", + "gdb", + "genero", + "genie", + "gettext", + "glsl", + "gml", + "gnuplot", + "go", + "groovy", + "gwbasic", + "haskell", + "hicest", + "68000devpac", + "hq9plus", + "html4strict", + "icon", + "idl", + "ini", + "inno", + "intercal", + "io", + "j", + "java", + "java5", + "javascript", + "jquery", + "6502kickass", + "kixtart", + "klonec", + "klonecpp", + "latex", + "lb", + "lisp", + "locobasic", + "logtalk", + "lolcode", + "lotusformulas", + "lotusscript", + "lscript", + "lsl2", + "lua", + "m68k", + "magiksf", + "make", + "mapbasic", + "matlab", + "mirc", + "mmix", + "modula2", + "modula3", + "mpasm", + "mxml", + "mysql", + "newlisp", + "nsis", + "oberon2", + "objc", + "objeck", + "ocaml", + "ocaml-brief", + "oobas", + "oracle11", + "oracle8", + "oxygene", + "oz", + "pascal", + "pcre", + "per", + "perl", + "perl6", + "pf", + "php", + "php-brief", + "pic16", + "pike", + "pixelbender", + "plsql", + "postgresql", + "povray", + "powerbuilder", + "powershell", + "progress", + "prolog", + "properties", + "providex", + "purebasic", + "python", + "q", + "qbasic", + "rails", + "rebol", + "reg", + "robots", + "rpmspec", + "rsplus", + "ruby", + "sas", + "scala", + "scheme", + "scilab", + "sdlbasic", + "smalltalk", + "smarty", + "sql", + "systemverilog", + "6502tasm", + "tcl", + "teraterm", + "text", + "thinbasic", + "tsql", + "typoscript", + "unicon", + "vala", + "vb", + "vbnet", + "verilog", + "vhdl", + "vim", + "visualfoxpro", + "visualprolog", + "whitespace", + "whois", + "winbatch", + "xbasic", + "xml", + "xorg_conf", + "xpp", + "z80", + "zxbasic"] + validClipboardSelectionOpts = ['primary', 'secondary', 'clipboard'] + validPrivateOpts = ['yes', 'no'] + ext2lang_map = {'sh': 'bash', 'bash': 'bash', 'bat': 'bat', 'c': 'c', 'h': 'c', 'cpp': 'cpp', 'css': 'css', 'html': 'html4strict', 'htm': 'html4strict', 'ini': 'ini', 'java': 'java', 'js': 'javascript', + 'jsp': 'java', 'pl': 'perl', 'php': 'php', 'php3': 'php', 'py': 'python', 'rb': 'ruby', 'rhtml': 'html4strict', 'sql': 'sql', 'sqlite': 'sql', 'tcl': 'tcl', 'vim': 'vim', 'xml': 'xml'} usage = """\ Usage: %%prog [OPTION]... [FILE]... @@ -509,30 +534,45 @@ def main(): %%prog --sysinfo -d "my laptop" --confirm %%prog -n codemonkey -d "problem with foo" -l python foo.py""" % FPASTE_URL - parser = OptionParser(usage=usage, version='%prog '+VERSION) - parser.add_option('', '--debug', dest='debug', help=SUPPRESS_HELP, action="store_true", default=False) + parser = OptionParser(usage=usage, version='%prog ' + VERSION) + parser.add_option('', '--debug', dest='debug', + help=SUPPRESS_HELP, action="store_true", default=False) parser.add_option('', '--proxy', dest='proxy', help=SUPPRESS_HELP) # pastebin-specific options first fpasteOrg_group = OptionGroup(parser, "fpaste.org Options") - fpasteOrg_group.add_option('-n', dest='nick', help='your nickname; default is "%default";', metavar='"NICKNAME"') - fpasteOrg_group.add_option('-l', dest='lang', help='language of content for syntax highlighting; default is "%default"; use "list" to show all ' + str(len(validSyntaxOpts)) + ' supported langs', metavar='"LANGUAGE"') - fpasteOrg_group.add_option('-x', dest='expires', help='time before paste is removed; default is %default seconds; valid options: ' + ', '.join(validExpiresOpts), metavar='EXPIRES') - fpasteOrg_group.add_option('-P', '--private', help='make paste private; default is %default; valid options: ' + ', '.join(validPrivateOpts), dest='make_private', metavar='"PRIVATE"') - fpasteOrg_group.add_option('-U', '--URL', help='URL of fpaste server; default is %default', dest='url', metavar='"FPASTE URL"') - fpasteOrg_group.add_option('-d', '--password', help='password for paste; default is %default', dest='password', metavar='"PASSWORD"') + fpasteOrg_group.add_option( + '-n', dest='nick', help='your nickname; default is "%default";', metavar='"NICKNAME"') + fpasteOrg_group.add_option('-l', dest='lang', help='language of content for syntax highlighting; default is "%default"; use "list" to show all ' + + str(len(validSyntaxOpts)) + ' supported langs', metavar='"LANGUAGE"') + fpasteOrg_group.add_option('-x', dest='expires', help='time before paste is removed; default is %default seconds; valid options: ' + + ', '.join(validExpiresOpts), metavar='EXPIRES') + fpasteOrg_group.add_option('-P', '--private', help='make paste private; default is %default; valid options: ' + + ', '.join(validPrivateOpts), dest='make_private', metavar='"PRIVATE"') + fpasteOrg_group.add_option( + '-U', '--URL', help='URL of fpaste server; default is %default', dest='url', metavar='"FPASTE URL"') + fpasteOrg_group.add_option( + '-d', '--password', help='password for paste; default is %default', dest='password', metavar='"PASSWORD"') parser.add_option_group(fpasteOrg_group) # other options fpasteProg_group = OptionGroup(parser, "Input/Output Options") - fpasteProg_group.add_option('-i', '--clipin', dest='clipin', help='read paste text from current X clipboard selection [requires: xsel]', action="store_true", default=False) - fpasteProg_group.add_option('-o', '--clipout', dest='clipout', help='save returned paste URL to X clipboard', action="store_true", default=False) - fpasteProg_group.add_option('', '--selection', dest='selection', help='specify which X clipboard to use. valid options: "primary" (default; middle-mouse-button paste), "secondary" (uncommon), or "clipboard" (ctrl-v paste)', metavar='CLIP') - fpasteProg_group.add_option('', '--fullpath', dest='fullpath', help='use pathname VS basename for file description(s)', action="store_true", default=False) - fpasteProg_group.add_option('', '--pasteself', dest='pasteself', help='paste this script itself', action="store_true", default=False) - fpasteProg_group.add_option('', '--sysinfo', dest='sysinfo', help='paste system information', action="store_true", default=False) - fpasteProg_group.add_option('', '--printonly', dest='printonly', help='print paste, but do not send', action="store_true", default=False) - fpasteProg_group.add_option('', '--confirm', dest='confirm', help='print paste, and prompt for confirmation before sending', action="store_true", default=False) + fpasteProg_group.add_option('-i', '--clipin', dest='clipin', + help='read paste text from current X clipboard selection [requires: xsel]', action="store_true", default=False) + fpasteProg_group.add_option('-o', '--clipout', dest='clipout', + help='save returned paste URL to X clipboard', action="store_true", default=False) + fpasteProg_group.add_option('', '--selection', dest='selection', + help='specify which X clipboard to use. valid options: "primary" (default; middle-mouse-button paste), "secondary" (uncommon), or "clipboard" (ctrl-v paste)', metavar='CLIP') + fpasteProg_group.add_option('', '--fullpath', dest='fullpath', + help='use pathname VS basename for file description(s)', action="store_true", default=False) + fpasteProg_group.add_option('', '--pasteself', dest='pasteself', + help='paste this script itself', action="store_true", default=False) + fpasteProg_group.add_option('', '--sysinfo', dest='sysinfo', + help='paste system information', action="store_true", default=False) + fpasteProg_group.add_option('', '--printonly', dest='printonly', + help='print paste, but do not send', action="store_true", default=False) + fpasteProg_group.add_option('', '--confirm', dest='confirm', + help='print paste, and prompt for confirmation before sending', action="store_true", default=False) parser.add_option_group(fpasteProg_group) # Let default be anonymous. @@ -543,7 +583,8 @@ def main(): # else: # print >> sys.stderr, "WARNING Could not run whoami. Posting anonymously." - parser.set_defaults(nick='', lang='text', make_private='yes', expires='2592000', selection='primary', password='', url=FPASTE_URL ) + parser.set_defaults(nick='', lang='text', make_private='yes', + expires='2592000', selection='primary', password='', url=FPASTE_URL) (options, args) = parser.parse_args() # Check for trailing slash @@ -558,20 +599,26 @@ def main(): if options.clipin: if not os.access('/usr/bin/xsel', os.X_OK): # TODO: try falling back to xclip or dbus - parser.error('OOPS - the clipboard options currently depend on "/usr/bin/xsel", which does not appear to be installed') + parser.error( + 'OOPS - the clipboard options currently depend on "/usr/bin/xsel", which does not appear to be installed') if options.clipin and args: - parser.error("Sending both clipboard contents AND files is not supported. Use -i OR filename(s)") + parser.error( + "Sending both clipboard contents AND files is not supported. Use -i OR filename(s)") for optk, optv, opts in [('language', options.lang, validSyntaxOpts), ('expires', options.expires, validExpiresOpts), ('clipboard selection', options.selection, validClipboardSelectionOpts)]: if optv not in opts: - parser.error("'%s' is not a valid %s option.\n\tVALID OPTIONS: %s" % (optv, optk, ', '.join(opts))) + parser.error("'%s' is not a valid %s option.\n\tVALID OPTIONS: %s" % ( + optv, optk, ', '.join(opts))) fileargs = args if options.fullpath: fileargs = [os.path.abspath(x) for x in args] else: - fileargs = [os.path.basename(x) for x in args] # remove potentially non-anonymous path info from file path descriptions + # remove potentially non-anonymous path info from file path + # descriptions + fileargs = [os.path.basename(x) for x in args] - #guess lang for some common file extensions, if all file exts similar, and lang not changed from default + # guess lang for some common file extensions, if all file exts similar, + # and lang not changed from default if options.lang == 'text': all_exts_similar = False for i in range(0, len(args)): @@ -584,17 +631,20 @@ def main(): if all_exts_similar and ext in ext2lang_map.keys(): options.lang = ext2lang_map[ext] - # get input from mutually exclusive sources, though they *could* be combined + # get input from mutually exclusive sources, though they *could* be + # combined text = "" if options.clipin: xselcmd = 'xsel -o --%s' % options.selection #text = os.popen(xselcmd).read() - p = subprocess.Popen(xselcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(xselcmd, shell=True, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) (text, err) = p.communicate() if p.returncode != 0: if options.debug: print >> sys.stderr, err - parser.error("'xsel' failure. this usually means you're not running X") + parser.error( + "'xsel' failure. this usually means you're not running X") if not text: parser.error("%s clipboard is empty" % options.selection) elif options.pasteself: @@ -615,7 +665,8 @@ def main(): parser.error("file '%s' is not readable" % f) if (len(args) > 1): # separate multiple files with header text += '#' * 78 + '\n' - text += '### file %d of %d: %s\n' % (i+1, len(args), fileargs[i]) + text += '### file %d of %d: %s\n' % ( + i + 1, len(args), fileargs[i]) text += '#' * 78 + '\n' text += open(f).read() if options.debug: @@ -626,7 +677,9 @@ def main(): if options.printonly or options.confirm: try: if is_text(text): - print text # when piped to less, sometimes fails with [Errno 32] Broken pipe + # when piped to less, sometimes fails with [Errno 32] Broken + # pipe + print text else: print "DATA" except IOError: @@ -646,7 +699,8 @@ def main(): else: xselcmd = 'xsel -i --%s' % options.selection #os.popen(xselcmd, 'wb').write(url) - p = subprocess.Popen(xselcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen(xselcmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, stdin=subprocess.PIPE) (out, err) = p.communicate(input=url) if p.returncode != 0: if options.debug: @@ -659,13 +713,14 @@ def main(): print >> sys.stderr, "WARNING: Could not shorten URL" print url else: - print short_url + " -> " + url + print short_url + " -> " + url else: sys.exit(1) if options.pasteself: - print >> sys.stderr, "install fpaste to local ~/bin dir by running: mkdir -p ~/bin; curl " + url + "raw/ -o ~/bin/fpaste && chmod +x ~/bin/fpaste" + print >> sys.stderr, "install fpaste to local ~/bin dir by running: mkdir -p ~/bin; curl " + \ + url + "raw/ -o ~/bin/fpaste && chmod +x ~/bin/fpaste" if __name__ == '__main__': sys.exit(0) From 4347659ff3671deefa62173e7e5a6cb221e697a1 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 08:21:43 +0530 Subject: [PATCH 5/7] Added Features - Last seen - Last few lines --- ekan0ra.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index 5e827cb..c84c8c1 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -10,8 +10,12 @@ import os import datetime import config as conf + import json + +from collections import deque + import fpaste commands = [ @@ -25,6 +29,8 @@ ('startclass', 'start logging the class'), ('endclass', 'ends logging the class'), ('pingall:[message]', 'pings the message to all'), + ('lastwords:[nick]','show last 10 lines of the user'), + ('lastseen:[nick]','shows last seen datetime'), ('help', 'list all the commands'), ('.link [portal]', 'Returns the link of the portal') ] @@ -64,6 +70,8 @@ def __init__(self, channel): self.qs_queue = [] self.links_reload() self.logger = None + self.lastseen = {} + self.lastspoken = {} def clearqueue(self): self.qs_queue = [] @@ -82,15 +90,14 @@ def startlogging(self, user, msg): time.asctime(time.localtime(time.time()))) user = user.split('!', 1)[0] self.logger.log("<%s> %s" % (user, msg)) - self.islogging = True + self.islogging = False def stoplogging(self, channel): if not self.logger: return - self.logger.log("[## Class Ended at %s ##]" % - time.asctime(time.localtime(time.time()))) + self.logger.log("[## Class Ended at %s ##]" % time.asctime(time.localtime(time.time()))) self.logger.close() - # self.upload_logs(channel) + self.upload_logs(channel) self.islogging = False def connectionLost(self, reason): @@ -117,6 +124,7 @@ def links_reload(self): def privmsg(self, user, channel, msg): """This will get called when the bot receives a message.""" user = user.split('!', 1)[0] + self.updateLastSeen(user) if self.islogging: user = user.split('!', 1)[0] self.logger.log("<%s> %s" % (user, msg)) @@ -173,6 +181,21 @@ def privmsg(self, user, channel, msg): for command, help_txt in commands: self.msg(user, help_template.format(command=command, help_text=help_txt)) + if msg.startswith('lastwords'): + nick = msg.split(':')[-1] + if nick in self.lastspoken: + for i in self.lastspoken[nick]: + self.msg(channel,i) + + if msg.startswith('lastseen'): + nick = msg.split(':')[-1] + self.names(channel).addCallback(self.activityTracker,nick=nick,channel=channel) + + if user in self.lastspoken: + self.lastspoken[user].append(msg) + else: + self.lastspoken[user] = deque(maxlen=10) + self.lastspoken[user].append(msg) if channel == self.nickname: @@ -198,6 +221,7 @@ def action(self, user, channel, msg): user = user.split('!', 1)[0] if self.islogging: self.logger.log("* %s %s" % (user, msg)) + pass # irc callbacks @@ -207,6 +231,32 @@ def irc_NICK(self, prefix, params): new_nick = params[0] if self.islogging: self.logger.log("%s is now known as %s" % (old_nick, new_nick)) + pass + + def userLeft(self,user,channel): + self.updateLastSeen(user) + + def userQuit(self,user,quitMessage): + self.updateLastSeen(user) + + def uesrJoined(self,nick,channel): + self.updateLastSeen(user) + + def updateLastSeen(self,user): + self.lastseen[user]=datetime.datetime.now().strftime('%c') + + def activityTracker(self,nicklist,nick,channel): + if nick in nicklist: + if self.lastseen.get(nick): + self.msg(channel, "%s is online now, last activity at %s" % (nick,self.lastseen[nick])) + else: + self.msg(channel, "%s is online now, last activity not known" % (nick)) + + else: + if nick in self.lastseen: + self.msg(channel,"last seen activity was on %s"%self.lastseen[nick]) + else: + self.msg(channel,"no data found") # For fun, override the method that determines how a nickname is changed on # collisions. The default method appends an underscore. From cc1df5deef5dd35e0a7880daa20d5dcc0a0c2842 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 08:58:27 +0530 Subject: [PATCH 6/7] Added Feature - Added spell correction command --- ekan0ra.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index c84c8c1..beeae53 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -176,6 +176,14 @@ def privmsg(self, user, channel, msg): lambda x: x != name, self.channel_admin) except Exception, err: print err + + if msg.startswith('s\\'): + wordlist = msg.split('\\')[1::] + line = self.lastspoken[user][-1] + for target,replace in zip(wordlist[0::2],wordlist[1::2]): + line = line.replace(target,replace) + statement = "what {user} meant is , {line}".format(user=user,line=line) + self.msg(channel,statement) if msg == 'help': for command, help_txt in commands: @@ -184,8 +192,8 @@ def privmsg(self, user, channel, msg): if msg.startswith('lastwords'): nick = msg.split(':')[-1] if nick in self.lastspoken: - for i in self.lastspoken[nick]: - self.msg(channel,i) + for line in self.lastspoken[nick]: + self.msg(channel,line) if msg.startswith('lastseen'): nick = msg.split(':')[-1] From 3834425da7ef091281b32938d25d94b4a487def8 Mon Sep 17 00:00:00 2001 From: Ghost-script Date: Sat, 26 Sep 2015 09:02:47 +0530 Subject: [PATCH 7/7] code cleaning --- ekan0ra.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ekan0ra.py b/ekan0ra.py index beeae53..05db2b1 100644 --- a/ekan0ra.py +++ b/ekan0ra.py @@ -229,7 +229,7 @@ def action(self, user, channel, msg): user = user.split('!', 1)[0] if self.islogging: self.logger.log("* %s %s" % (user, msg)) - pass + # irc callbacks @@ -239,7 +239,7 @@ def irc_NICK(self, prefix, params): new_nick = params[0] if self.islogging: self.logger.log("%s is now known as %s" % (old_nick, new_nick)) - pass + def userLeft(self,user,channel): self.updateLastSeen(user)