From 71f3532e04e5c76327363a3fd36b506f54e5043d Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Sat, 21 Nov 2015 22:47:52 +0100
Subject: degesch: add the first Lua plugin to distribution
This required separate plugin directories for both pluginized executables.
---
plugins/pomodoro | 502 -------------------------------------------------------
1 file changed, 502 deletions(-)
delete mode 100755 plugins/pomodoro
(limited to 'plugins/pomodoro')
diff --git a/plugins/pomodoro b/plugins/pomodoro
deleted file mode 100755
index 8f30327..0000000
--- a/plugins/pomodoro
+++ /dev/null
@@ -1,502 +0,0 @@
-#!/usr/bin/env ruby
-# coding: utf-8
-#
-# ZyklonB pomodoro plugin
-#
-# Copyright 2015 Přemysl Janouch
-# See the file LICENSE for licensing information.
-#
-
-# --- Simple event loop --------------------------------------------------------
-
-# This is more or less a straight-forward port of my C event loop. It's a bit
-# unfortunate that I really have to implement all this in order to get some
-# basic asynchronicity but at least I get to exercise my Ruby.
-
-class TimerEvent
- attr_accessor :index, :when, :callback
-
- def initialize (callback)
- raise ArgumentError unless callback.is_a? Proc
-
- @index = nil
- @when = nil
- @callback = callback
- end
-
- def active?
- @index != nil
- end
-
- def until
- return @when - Time.new
- end
-end
-
-class IOEvent
- READ = 1 << 0
- WRITE = 1 << 1
-
- attr_accessor :read_index, :write_index, :io, :callback
-
- def initialize (io, callback)
- raise ArgumentError unless callback.is_a? Proc
-
- @read_index = nil
- @write_index = nil
- @io = io
- @callback = callback
- end
-end
-
-class EventLoop
- def initialize
- @running = false
- @timers = []
- @readers = []
- @writers = []
- @io_to_event = {}
- end
-
- def set_timer (timer, timeout)
- raise ArgumentError unless timer.is_a? TimerEvent
-
- timer.when = Time.now + timeout
- if timer.index
- heapify_down timer.index
- heapify_up timer.index
- else
- timer.index = @timers.size
- @timers.push timer
- heapify_up timer.index
- end
- end
-
- def reset_timer (timer)
- raise ArgumentError unless timer.is_a? TimerEvent
- remove_timer_at timer.index if timer.index
- end
-
- def set_io (io_event, events)
- raise ArgumentError unless io_event.is_a? IOEvent
- raise ArgumentError unless events.is_a? Numeric
-
- reset_io io_event
-
- @io_to_event[io_event.io] = io_event
- if events & IOEvent::READ
- io_event.read_index = @readers.size
- @readers.push io_event.io
- end
- if events & IOEvent::WRITE
- io_event.read_index = @writers.size
- @writers.push io_event.io
- end
- end
-
- def reset_io (io_event)
- raise ArgumentError unless io_event.is_a? IOEvent
-
- @readers.delete_at io_event.read_index if io_event.read_index
- @writers.delete_at io_event.write_index if io_event.write_index
-
- io_event.read_index = nil
- io_event.write_index = nil
-
- @io_to_event.delete io_event.io
- end
-
- def run
- @running = true
- while @running do one_iteration end
- end
-
- def quit
- @running = false
- end
-
-private
- def one_iteration
- rs, ws, = IO.select @readers, @writers, [], nearest_timeout
- dispatch_timers
- (Array(rs) | Array(ws)).each do |io|
- @io_to_event[io].callback.call io
- end
- end
-
- def dispatch_timers
- now = Time.new
- while not @timers.empty? and @timers[0].when <= now do
- @timers[0].callback.call
- remove_timer_at 0
- end
- end
-
- def nearest_timeout
- return nil if @timers.empty?
- timeout = @timers[0].until
- if timeout < 0 then 0 else timeout end
- end
-
- def remove_timer_at (index)
- @timers[index].index = nil
- moved = @timers.pop
- return if index == @timers.size
-
- @timers[index] = moved
- @timers[index].index = index
- heapify_down index
- end
-
- def swap_timers (a, b)
- @timers[a], @timers[b] = @timers[b], @timers[a]
- @timers[a].index = a
- @timers[b].index = b
- end
-
- def heapify_up (index)
- while index != 0 do
- parent = (index - 1) / 2
- break if @timers[parent].when <= @timers[index].when
- swap_timers index, parent
- index = parent
- end
- end
-
- def heapify_down (index)
- loop do
- parent = index
- left = 2 * index + 1
- right = 2 * index + 2
-
- lowest = parent
- lowest = left if left < @timers.size and
- @timers[left] .when < @timers[lowest].when
- lowest = right if right < @timers.size and
- @timers[right].when < @timers[lowest].when
- break if parent == lowest
-
- swap_timers lowest, parent
- index = lowest
- end
- end
-end
-
-# --- IRC protocol -------------------------------------------------------------
-
-$stdin.set_encoding 'ASCII-8BIT'
-$stdout.set_encoding 'ASCII-8BIT'
-
-$stdin.sync = true
-$stdout.sync = true
-
-$/ = "\r\n"
-$\ = "\r\n"
-
-RE_MSG = /(?::([^! ]*)(?:!([^@]*)@([^ ]*))? +)?([^ ]+)(?: +(.*))?$/
-RE_ARGS = /:?((?<=:).*|[^ ]+) */
-
-def parse (line)
- m = line.match RE_MSG
- return nil if not m
-
- nick, user, host, command, args = *m.captures
- args = if args then args.scan(RE_ARGS).flatten else [] end
- [nick, user, host, command, args]
-end
-
-def bot_print (what)
- print "ZYKLONB print :#{what}"
-end
-
-# --- Initialization -----------------------------------------------------------
-
-# We can only read in configuration from here so far
-# To read it from anywhere else, it has to be done asynchronously
-$config = {}
-[:prefix].each do |name|
- print "ZYKLONB get_config :#{name}"
- _, _, _, _, args = *parse($stdin.gets.chomp)
- $config[name] = args[0]
-end
-
-print "ZYKLONB register"
-
-# --- Plugin logic -------------------------------------------------------------
-
-# FIXME: this needs a major refactor as it doesn't make much sense at all
-
-class MessageMeta < Struct.new(:nick, :user, :host, :channel, :ctx, :quote)
- def respond (message)
- print "PRIVMSG #{ctx} :#{quote}#{message}"
- end
-end
-
-class Context
- attr_accessor :nick, :ctx
-
- def initialize (meta)
- @nick = meta.nick
- @ctx = meta.ctx
- end
-
- def == (other)
- self.class == other.class \
- and other.nick == @nick \
- and other.ctx == @ctx
- end
-
- alias eql? ==
-
- def hash
- @nick.hash ^ @ctx.hash
- end
-end
-
-class PomodoroTimer
- def initialize (context)
- @ctx = context.ctx
- @nicks = [context.nick]
-
- @timer_work = TimerEvent.new(lambda { on_work })
- @timer_rest = TimerEvent.new(lambda { on_rest })
-
- on_work
- end
-
- def inform (message)
- # FIXME: it tells the nick even in PM's
- quote = "#{@nicks.join(" ")}: "
- print "PRIVMSG #{@ctx} :#{quote}#{message}"
- end
-
- def on_work
- inform "work now!"
- $loop.set_timer @timer_rest, 25 * 60
- end
-
- def on_rest
- inform "rest now!"
- $loop.set_timer @timer_work, 5 * 60
- end
-
- def join (meta)
- return if @nicks.include? meta.nick
-
- meta.respond "you have joined their pomodoro"
- @nicks |= [meta.nick]
- end
-
- def part (meta, requested)
- return if not @nicks.include? meta.nick
-
- if requested
- meta.respond "you have stopped your pomodoro"
- end
-
- @nicks -= [meta.nick]
- if @nicks.empty?
- $loop.reset_timer @timer_work
- $loop.reset_timer @timer_rest
- end
- end
-
- def status (meta)
- return if not @nicks.include? meta.nick
-
- if @timer_rest.active?
- till = @timer_rest.until
- meta.respond "working, #{(till / 60).to_i} minutes, " +
- "#{(till % 60).to_i} seconds until rest"
- end
- if @timer_work.active?
- till = @timer_work.until
- meta.respond "resting, #{(till / 60).to_i} minutes, " +
- "#{(till % 60).to_i} seconds until work"
- end
- end
-end
-
-class Pomodoro
- KEYWORD = "pomodoro"
-
- def initialize
- @timers = {}
- end
-
- def on_help (meta, args)
- meta.respond "usage: #{KEYWORD} { start | stop | join | status }"
- end
-
- def on_start (meta, args)
- if args.size != 0
- meta.respond "usage: #{KEYWORD} start"
- return
- end
-
- context = Context.new meta
- if @timers[context]
- meta.respond "you already have a timer running here"
- else
- @timers[context] = PomodoroTimer.new meta
- end
- end
-
- def on_join (meta, args)
- if args.size != 1
- meta.respond "usage: #{KEYWORD} join "
- return
- end
-
- context = Context.new meta
- if @timers[context]
- meta.respond "you already have a timer running here"
- return
- end
-
- joined_context = Context.new meta
- joined_context.nick = args.shift
- timer = @timers[joined_context]
- if not timer
- meta.respond "that person doesn't have a timer here"
- else
- timer.join meta
- @timers[context] = timer
- end
- end
-
- def on_stop (meta, args)
- if args.size != 0
- meta.respond "usage: #{KEYWORD} stop"
- return
- end
-
- context = Context.new meta
- timer = @timers[context]
- if not timer
- meta.respond "you don't have a timer running here"
- else
- timer.part meta, true
- @timers.delete context
- end
- end
-
- def on_status (meta, args)
- if args.size != 0
- meta.respond "usage: #{KEYWORD} status"
- return
- end
-
- timer = @timers[Context.new meta]
- if not timer
- meta.respond "you don't have a timer running here"
- else
- timer.status meta
- end
- end
-
- def process_command (meta, msg)
- args = msg.split
- return if args.shift != KEYWORD
-
- method = "on_#{args.shift}"
- send method, meta, args if respond_to? method
- end
-
- def on_server_nick (meta, command, args)
- # TODO: either handle this properly...
- happened = false
- @timers.keys.each do |key|
- next if key.nick != meta.nick
- @timers[key].part meta, false
- @timers.delete key
- happened = true
- end
- if happened
- # TODO: ...or at least inform the user via his new nick
- end
- end
-
- def on_server_part (meta, command, args)
- # TODO: instead of cancelling the user's pomodoros, either redirect
- # them to PM's and later upon rejoining undo the redirection...
- context = Context.new(meta)
- context.ctx = meta.channel
- if @timers.include? context
- # TODO: ...or at least inform the user about the cancellation
- @timers[context].part meta, false
- @timers.delete context
- end
- end
-
- def on_server_quit (meta, command, args)
- @timers.keys.each do |key|
- next if key.nick != meta.nick
- @timers[key].part meta, false
- @timers.delete key
- end
- end
-
- def process (meta, command, args)
- method = "on_server_#{command.downcase}"
- send method, meta, command, args if respond_to? method
- end
-end
-
-# --- IRC message processing ---------------------------------------------------
-
-$handlers = [Pomodoro.new]
-def process_line (line)
- msg = parse line
- return if not msg
-
- nick, user, host, command, args = *msg
-
- context = nick
- quote = ""
- channel = nil
-
- if args.size >= 1 and args[0].start_with? ?#, ?+, ?&, ?!
- case command
- when "PRIVMSG", "NOTICE", "JOIN"
- context = args[0]
- quote = "#{nick}: "
- channel = args[0]
- when "PART"
- channel = args[0]
- end
- end
-
- # Handle any IRC message
- meta = MessageMeta.new(nick, user, host, channel, context, quote).freeze
- $handlers.each do |handler|
- handler.process meta, command, args
- end
-
- # Handle pre-processed bot commands
- if command == 'PRIVMSG' and args.size >= 2
- msg = args[1]
- return unless msg.start_with? $config[:prefix]
- $handlers.each do |handler|
- handler.process_command meta, msg[$config[:prefix].size..-1]
- end
- end
-end
-
-buffer = ""
-stdin_io = IOEvent.new($stdin, lambda do |io|
- begin
- buffer << io.read_nonblock(4096)
- lines = buffer.split $/, -1
- buffer = lines.pop
- lines.each { |line| process_line line }
- rescue EOFError
- $loop.quit
- rescue IO::WaitReadable
- # Ignore
- end
-end)
-
-$loop = EventLoop.new
-$loop.set_io stdin_io, IOEvent::READ
-$loop.run
--
cgit v1.2.3-70-g09d2