1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
|
# -*- encoding: iso-8859-1 -*-
#
# Copyright (c) 2006 by EgS <i@egs.name>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
#######################################################################
# #
# This script enables the execution of events triggered commands #
# where the events are attach or detach of the screen weechat #
# is running in. #
# #
# Name: OnAttach #
# Licence: GPL v2 #
# Author: Marcus Eggenberger <i@egs.name> #
# #
# Usage: #
# /onattach or /ondetach to add new events #
# /screenevents to manage events #
# #
# use /help command for detailed information #
# #
# Changelog: #
# 0.5: added screen guessing to backup screen-detection #
# 0.4: fixed TypeError in weechat 0.1.9 #
# 0.3: now checking on startup if weechat runs in a screen #
# 0.2: added usage of get/set_plugin_config to store eventlist #
# 0.1: first version released #
# #
#######################################################################
# ====================
# Imports
# ====================
import os
import sys
# ====================
# Constants
# ====================
NAME = 'OnAttach'
VERSION = '0.5'
DESCRIPTION = "Executing commands on screen Attach/Detach"
OFF = False
ON = True
# ====================
# Exceptions
# ====================
class OnAttachError(Exception):
pass
# ====================
# Helpers
# ====================
class WeePrint(object):
def write(self, text):
text = text.rstrip(' \0\n') # strip the null byte appended by pythons print
if text:
weechat.prnt(text,'')
def getScreenByPpid():
# the screen we are in should be our parents parent
# aka something like:
# SCREEN
# \_ -/bin/bash
# \_ weechat-curses
ppid = os.getppid()
# get SCREEN pid
pipe = os.popen("ps o ppid= -p %d" % ppid)
screenpid = pipe.read().strip()
pipe.close()
# check if screen pid really belongs to a screen
pipe = os.popen("ps o cmd= -p %s" % screenpid)
cmd = pipe.read().strip()
pipe.close()
if 'screen' not in cmd.lower():
raise OnAttachError
else:
return screenpid
def getScreenByList():
# checks if there is only one attached screen
# if so use: it! ;)
pipe = os.popen("screen -list | grep -i attached")
screens = pipe.read().strip()
pipe.close()
screens = screens.splitlines()
if len(screens) > 1:
print "There are more then one screen currently attached."
print "Detach all other running screens and reload OnAttach"
print "to ensure correct screen detection."
raise OnAttachError
try:
socket, status = screens[0].split()
except:
# thats no common screen list...
print "failed!"
raise OnAttachError
else:
print "Using screen %s" % socket
return socket
def getScreenPid():
try:
return getScreenByPpid()
except OnAttachError:
print "!!! Unable to determine screen by parentid!"
print "Trying to guess screen..."
return getScreenByList()
def registerFunction(function):
# Register a python function as a commandhandler
# Function needs to be named like weeFunction and
# is bound to /function
# docstring is used for weechat help
functionname = function.__name__ # guess what :)
weecommand = functionname[3:].lower() # strip the wee
doc = function.__doc__.splitlines()
arguments = doc[0] # First docstring line is the arguments string
description = doc[1][4:]
args_description = '\n'.join([line[4:] for line in doc[2:-1]]) # join rest and strip indentation
if not function.func_defaults: # use args default value as template
template = ''
elif len(function.func_defaults) == 1:
template = function.func_defaults[0]
elif len(function.func_defaults) == 2:
template = func.func_defaults[1]
weechat.add_command_handler(weecommand, functionname, description, arguments, args_description, template)
def registerFunctions():
functions = [function for name, function in globals().iteritems() if name.startswith('wee') and callable(function)]
for func in functions:
registerFunction(func)
# ====================
# Classes
# ====================
class Event(object):
ESCAPECHAR = '\\'
SEPARATOR = ';'
def __init__(self, delay, step, channel, server, command, activehigh=True):
self.delay = int(delay)
self.step = step
self.channel = channel
self.server = server
self.command = command
self.activestate = activehigh
self.reset()
#@classmethod
def unserialize(cls, step, serial):
try:
# let's try the easy way :)
delay, channel, server, command, activestate = serial.split()
except ValueError:
# ok we got an escaped separator... :/
data = serial.split(cls.SEPARATOR)
for i in range(len(data)):
if data[i].endswith(cls.ESCAPECHAR):
data[i+1] = data[i] + cls.SEPARATOR + data[i+1]
data = [item.replace(cls.ESCAPECHAR + cls.SEPARATOR, cls.SEPARATOR) for item in data if not item.endswith(cls.ESCAPECHAR)]
delay, channel, server, command, activestate = data
delay = int(delay)
activestate = activestate == 'True'
return cls(delay, step, channel, server, command, activestate)
# lets go for 2.3 compatiblity
unserialize = classmethod(unserialize)
#@property
def serialized(self):
data = [self.delay, self.channel, self.server, self.command, self.activestate]
data = [str(item).replace(self.SEPARATOR, self.ESCAPECHAR + self.SEPARATOR) for item in data]
return self.SEPARATOR.join(data)
# lets go for 2.3 compatiblity
serialized = property(serialized)
def reset(self):
self.currentdelay = self.delay
self.fired = False
def fire(self):
weechat.command(self.command, self.channel, self.server)
self.fired = True
def stimulus(self, state):
if state != self.activestate:
self.reset()
else:
self.currentdelay -= self.step
if self.currentdelay <= 0 and not self.fired:
self.fire()
class CheckStatus(object):
ESCAPECHAR = '\\'
SEPARATOR = '|'
eventlist = []
step = 5
def __init__(self):
try:
self.screenpid = getScreenPid()
except OnAttachError:
raise
# try to read config data
serializedEvents = weechat.get_plugin_config("events")
if not serializedEvents:
return
events = serializedEvents.split(self.SEPARATOR)
for i in range(len(events)):
if events[i].endswith(self.ESCAPECHAR):
events[i+1] = events[i] + self.SEPARATOR + ss[i+1]
events = [event.replace(self.ESCAPECHAR + self.SEPARATOR, self.SEPARATOR) for event in events if not event.endswith(self.ESCAPECHAR)]
for event in events:
try:
self.eventlist.append(Event.unserialize(self.step, event))
except:
print "Unable to unserialize event!"
print "Try to add the event manualy and save config again."
def __call__(self, server="", args=""):
# check if the screen is attached or detatched
pipe = os.popen("screen -list | grep %s" % self.screenpid)
screenlist = pipe.read().strip()
pipe.close()
if 'attached' in screenlist.lower():
state = ON
elif 'detached' in screenlist.lower():
state = OFF
else:
print "Unable to determine screen status"
return weechat.PLUGIN_RC_KO
for event in self.eventlist:
event.stimulus(state)
return weechat.PLUGIN_RC_OK
def addEvent(self, delay, channel, server, command, activehigh=True):
event = Event(delay, self.step, channel, server, command, activehigh)
self.eventlist.append(event)
def showEvents(self):
format = "%2s | %-10s | %-10s | %-15s | %-7s | %-2s %s"
separator = "---+------------+------------+-----------------+---------+----"
print separator
print format % ("ID", "Server", "Channel", "Command", "Delay", "on", "")
print separator
for i in range(len(self.eventlist)):
event = self.eventlist[i]
if event.activestate:
atde = 'AT'
else:
atde = 'DE'
if event.fired:
fired = '*'
else:
fired = ''
print format % (i, event.server[:10], event.channel[:10], event.command[:15], "%3d sec" % event.delay, atde, fired)
print separator
def save(self):
weechat.set_plugin_config("events",self.serialized)
#@property
def serialized(self):
events = [str(event.serialized).replace(self.SEPARATOR, self.ESCAPECHAR + self.SEPARATOR) for event in self.eventlist]
return self.SEPARATOR.join(events)
# lets go for 2.3 compatiblity
serialized = property(serialized)
def dropEvent(self, eventid):
del self.eventlist[eventid]
# ====================
# Functions
# ====================
def addEvent(server, args, activehigh=True):
delay, channel, command = args.split(' ', 2)
delay = int(delay)
channel = channel.strip("'\"")
checkStatus.addEvent(delay, channel, server, command, activehigh)
if activehigh:
atde = "at"
else:
atde = "de"
print "Added %stach event '%s' for channel %s on network %s with %d seconds delay" % (atde, command, channel, server, delay)
def weeOnAttach(server, args):
"""delay channel command
Adds a command which is executed after attaching the screen
The command is executed <delay> seconds after the attach.
Use "" or '' as channel if the command should be executed in
the server buffer.
Example:
/onattach 180 &bitlbee account on 0
"""
try:
addEvent(server, args)
except:
weechat.command("/help onattach", "", server)
return 0
else:
return 1
def weeOnDetach(server, args):
"""delay channel command
Adds a command which is executed after detaching the screen
The command is executed <delay> seconds after the detach.
Use "" or '' as channel if the command should be executed in
the server buffer.
Example:
/ondetach 180 &bitlbee account on 0
"""
try:
addEvent(server, args, activehigh=False)
except:
weechat.command("/help onattach", "", server)
return 0
else:
return 1
def weeScreenevents(server, args="show|save|drop"):
"""show | save | drop eventid [eventid [...]]
shows, saves or drops events
show:
Shows all current active events
save:
Saves current events permanently
drop:
Delete event from onAttach/onDetach list.
Use /showevents to get eventid
"""
try:
action, args = args.split(' ',1)
except ValueError:
action = args.strip()
args = ''
actions = {'show':showEvents,
'drop':dropEvent,
'save':saveEvents}
try:
action = actions[action]
except KeyError:
weechat.command("/help screenevents", "", server)
return 0
else:
return action(server, args)
def showEvents(server, args):
checkStatus.showEvents()
return 1
def dropEvent(server, args):
try:
eventids = [int(id) for id in args.split()]
except:
print "eventid musst be a number!"
return 0
eventids.sort()
eventids.reverse()
for eventid in eventids:
try:
checkStatus.dropEvent(eventid)
except:
print "Unable to drop Event %d" % eventid
return 0
print "dropped %d events!" % len(eventids)
return 1
def saveEvents(server, args):
checkStatus.save()
return 1
# ====================
# Let's Register!
# ====================
if __name__ == '__main__':
try:
import weechat
except ImportError:
print "This script is to be loaded as PythonScript in WeeChat"
print "Get WeeChat now at: http://weechat.flashtux.org/"
import sys
sys.exit(10)
# kick pythons print to weechat.prnt(msg, '')
sys.stdout = WeePrint()
weechat.register(NAME, VERSION, "", DESCRIPTION)
try:
checkStatus = CheckStatus()
except OnAttachError:
print "!!! Requirements for %s not met:" % NAME
print "!!! - WeeChat is not running in a screen or not able to get screen PID"
print "!!! --> Run WeeChat in a screen to fix the problem!"
else:
weechat.add_timer_handler(checkStatus.step, "checkStatus")
registerFunctions()
|