examples/mcabberrc.lua
changeset 5 cba039bd6f13
child 6 90dceae3ed1f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/mcabberrc.lua	Mon Feb 23 23:23:42 2009 +0200
@@ -0,0 +1,456 @@
+
+
+--[[
+
+DESCRIPTION
+
+This is a demo config file to show, what you can do with lua.
+
+Feature list:
+XEP-0004 Forms parsing and filling
+XEP-0030 Info/items discovery requests (mcabber already can do replies)
+XEP-0047 In-Bound Byte Streams (sending, receiving, saving with specified name, rejecting)
+XEP-0077 In-Band Registration (only registration, but with data forms too)
+XEP-0146 Remote commands requests
+Jobs (actions, fired when some event occurs, now in one file, updated on exit)
+MPD status polling (can be turned off)
+Beep on all messages, even on chatroom ones
+Url saving to file (for urlview)
+Transported buddies availability indication
+Actions on multiple marked buddies
+Fallback commands (localized also! :) )
+Help for fallback commands (well, I know, that it can be done with mcabber's help system, but it requires access to system files...)
+All features have native mcabber interface
+
+REQUIREMENTS
+
+liblua-loudmouth (lm.lua and loudmouth.so)
+liblua-socket - mpd
+
+NOTES
+
+Most hooks get one incoming parameter - hash table with some fields in it.
+This allows further extending and adding arguments without breaking
+previous implementations.
+
+Ibb uses own iq handler. This is the laziest way to implement this.
+
+BUILTINS
+
+print (global) - prints to log
+dofile (global) - loads lua file from default mcabber location
+
+main methods:
+- run           - run literal mcabber command
+- beep          - beep
+- log           - print to log w/specified priority
+- print_info    - print info into specified buffer
+- config_file   - format full file name from relative to mcabber's config dir
+- status        - get current user status and message
+- roster        - get list of roster jids (rooms, buddies and agents)
+- current_buddy - get jid of current buddy
+- buddy_info    - get table with info about jid and its resources
+- connection    - get lightuserdata for mcabber's lm connection
+- timer         - run function periodically
+- bgread        - run command and read it's output in background
+- add_feature   - add string to feature list (for disco#info)
+- del_feature   - delete string from feature list
+- add_command   - adds mcabber command
+- del_command   - remove mcabber command
+
+--]]
+
+-- This is a hack to allow loading of lm.lua and loudmouth.so from ~/.mcabber
+-- instead of installing them system-wide
+package.path = main.config_file ( '?.lua' ) .. ';' .. package.path
+package.cpath = main.config_file ( '?.so' ) .. ';' .. package.cpath
+
+require 'lm'
+
+-- OPTIONS, COMMON SUPPORT ROUTINES
+
+url_file      = main.config_file ( 'urls.log' )
+transport_jid = 'icq.jabber.kiev.ua' -- TODO: allow multiple transports
+beep_enable   = false
+
+-- XXX: to C?
+char2status = {
+	f = 'free',
+	o = 'online',
+	a = 'away',
+	d = 'dnd',
+	n = 'notavail',
+	i = 'invisible',
+	['_'] = 'offline',
+	['?'] = 'message',
+}
+
+function shell_escape ( str )
+	if str then
+		return "'" .. str:gsub ( "'", "'\\''" ) .. "'"
+	else
+		return "''"
+	end
+end
+
+-- This is for debugging purposes, for real reloading need to quote and bracket keys.
+function table_to_string ( tab, pre )
+	local prefix = pre or ""
+	local tbls, jk = "", ""
+
+	for key, val in pairs ( tab ) do
+		if type ( val ) == 'table' then
+			tbls = string.format ( "%s  %s%s = %s,\n", tbls, prefix, tostring(key), table_to_string ( val, "  " .. prefix ) )
+		else
+			jk = string.format ( "%s %s = %q,", jk, tostring(key), tostring(val) )
+		end
+	end
+
+	if tbls == "" then
+		return string.format ( "{%s }", jk:sub ( 1, -2 ) )
+	else
+		return string.format ( "{%s\n%s%s}", jk, tbls, prefix )
+	end
+end
+
+-- XXX to C?
+function full_current_jid ()
+	local jid = main.current_buddy ()
+	if jid then
+		local info = main.buddy_info ( jid )
+		local prio, resource = 0
+		for res, par in pairs ( info.resources ) do
+			if prio <= par.priority then
+				resource = res
+				prio = par.priority
+			end
+		end
+		if resource then
+			return jid .. '/' .. resource
+		else
+			return jid
+		end
+	else
+		return nil
+	end
+end
+
+-- COMMANDS
+
+function yesno ( value )
+	if value == 'enable' or value == 'yes' or value == 'true' or value == 'on' or value == true then
+		return true
+	elseif value == 'disable' or value == 'no' or value == 'false' or value == 'off' or value == false then
+		return false
+	else
+		return nil
+	end
+end
+
+-- FIXME: eats spaces
+function parse_args ( args )
+	local ret = {}
+	local still_opts = true
+	local optname
+	local option = false
+	for word in args:gmatch ( "%S+" ) do
+		if still_opts and not option and word:sub ( 1, 1 ) == '-' then
+			option = true
+			optname = word:sub ( 2 )
+		elseif option then
+			ret[optname] = word
+			option = false
+		else
+			still_opts = false
+			table.insert ( ret, word )
+		end
+	end
+	return ret
+end
+
+-- Help strings should not contain command, only arguments. This is necessary to support soft aliases.
+commands_help = {
+	file      = "filename\n\nSends file as a message. Just shorthand.",
+	s         = "status [message]\n\nSets your status, but takes into account mpd (if enabled).",
+	beep      = "[enable|disable|on|off|yes|no|true|false]\n\nEnables or disables beeping on all messages.\nIf state is omitted, prints current state.",
+	cmd       = "shell_command\n\nRuns shell command in background and sends output to current buddy.\nWorks asynchroneously, and may break long output in the middle of line",
+	exthelp   = "[command]\n\nPrints help for a given command, or list of available help topics.",
+	reload    = "\n\nJust a shorthand to reload lua config file. Note, that for now this discards all changes to configuration, open forms, transferred files.",
+	['join!'] = "\n\nForcibly joins to current buddy. Just saves you typing of full room name (that can be quite long) in a case of a non-bookmarked rooms.",
+	count     = "\n\nPrints number of resources of current buddy. Useful to determine member count of large room."
+}
+
+main.add_command ( "lua",
+	function ( args )
+		assert ( loadstring ( args ) ) ()
+	end )
+main.add_command ( 'file',
+	function ( args )
+		main.run ( 'say_to -f ' .. args .. ' .' )
+	end )
+main.add_command ( 's',
+	function ( args )
+		main.run ( ('status %s %s'):format ( args, mpd_getstatus () ) )
+	end )
+main.add_command ( 'beep',
+	function ( args )
+		local enable = yesno ( args )
+		if enable == nil then
+			if beep_enable then
+				print ( "Beep on message is enabled" )
+			else
+				print ( "Beep on message is disabled" )
+			end
+		else
+			beep_enable = enable
+		end
+	end )
+main.add_command ( 'cmd',
+	function ( args )
+		local to = main.current_buddy ()
+		main.run ( ('send_to -q %q $ %s'):format ( to, args ) )
+		main.bgread ( args,
+			function ( data )
+				if data then
+					main.run ( ('send_to -q %q %s'):format ( to, data ) )
+					return true
+				else
+					return false
+				end
+			end )
+	end )
+main.add_command ( 'exthelp',
+	function ( args )
+		if commands_help[args] then
+			print ( "\n /" .. args .. ' ' .. commands_help[args] )
+		else
+			print ( "No help for this command." )
+			list = "Help available for commands: "
+			for k in pairs (commands_help) do
+				list = list .. k .. ', '
+			end
+			print ( list:sub ( 1, -3 ) )
+			print ( "For built-in mcabber commands see /help" )
+		end
+	end )
+main.add_command ( 'reload',
+	function ( args )
+		dofile ( main.config_file ( 'mcabberrc.lua' ) )
+	end )
+main.add_command ( 'join!',
+	function ( args )
+		main.run ( 'room join ' .. main.current_buddy () )
+	end )
+main.add_command ( 'count',
+	function ( args )
+		local count = 0
+		for resource in pairs ( main.buddy_info ( main.current_buddy () ).resources ) do
+			count = count + 1
+		end
+		print ( "Resource count: " .. count )
+	end )
+
+for k, arg in ipairs ( { ')', '/', '(', 'D', '-/', 'S', '1', ']', '[' } ) do
+	main.add_command ( arg,
+		function ( args )
+			main.run ( 'say :' .. arg .. ' ' .. args )
+		end )
+end
+
+-- MARKING
+
+dopath 'marking'
+
+-- MPD
+
+dopath 'mpd'
+
+-- FORMS (XEP-0004)
+
+dopath 'xep0004'
+
+-- DISCO (XEP-0030)
+
+dopath 'xep0030'
+
+-- IBB (XEP-0047)
+
+dopath 'xep0047'
+
+-- IN-BAND REGISTRATION (XEP-0077)
+
+dopath 'xep0077'
+
+-- REMOTE CONTROLLING CLIENTS (XEP-0146)
+
+dopath 'xep0146'
+
+-- JOBS
+
+delayed_jobs = {}
+
+-- FIXME: do only if it exists
+dopath 'jobs.lua'
+
+function save_jobs ()
+	local h = io.open ( main.config_file ( 'jobs.lua' ), "w" )
+	if not h then
+		print ( 'Cannot open jobs file for writing!' )
+		return
+	end
+	h:write ( "-- This is autogenerated file, do not edit it manually\n\ndelayed_jobs = {\n" );
+	for jid, more in pairs ( delayed_jobs ) do
+		h:write ( string.format ( "\t[%q] = {\n", jid ) )
+		for status, action in pairs ( more ) do
+			if action then -- remove fired jobs
+				h:write ( string.format ( "\t\t[%q] = %q,\n", status, action ) )
+			end
+		end
+		h:write ( "\t},\n" )
+	end
+	h:write ( "}\n" )
+	h:close ()
+end
+
+main.add_command ( 'delay',
+	function ( args )
+		args = parse_args ( args )
+		local who
+		if args.t then
+			who = args.t
+			args.t = nil
+		else
+			who = main.current_buddy ()
+		end
+		local stat = args[1]
+		args[1] = nil
+		delayed_jobs[who] = { }
+		delayed_jobs[who][stat] =
+			function ()
+				main.run ( 'say_to -q ' .. who .. ' ' .. rebuild_args_string ( args ) )
+			end
+	end )
+
+main.add_command ( 'job',
+	function ( args )
+		local action, jid, stat = args:match ( "(%w+)%s+(%w+)%s+(%w)" )
+		if action == 'del' then
+			delayed_jobs[jid][stat] = nil
+		else
+			print ( 'List of jobs:' )
+			for jid, jobs in pairs ( delayed_jobs ) do
+				for status in pairs ( jobs ) do
+					print ( ' - ' .. jid .. ' -> ' .. status )
+				end
+			end
+		end
+	end )
+
+commands_help['delay'] = "[-t target_jid] status_letter message\n\nDelays sending a message to target jid (or current buddy) until it switches to specified status."
+commands_help['job'] = "[del jid status_letter]\n\nLists available jobs or deletes specified one."
+
+-- HOOKS
+
+ibb_handler_registered = false
+
+-- Soft hooks, implemented through mcabber options
+function hook_post_connect ()
+	main.run ( 'group fold テフノ' )
+	main.run ( 'group fold にゃ' )
+	main.run ( 'group fold にゃ - Друзі' )
+
+	main.run ( 'color muc * on' )
+
+	main.run ( ("color roster * *@%s red"):format ( transport_jid ) )
+	main.run ( ("color roster dn_? *@%s red"):format ( transport_jid ) )
+
+	if mpd_enabled then
+		mpd_callback ()
+	end
+
+	-- FIXME
+	if not ibb_handler_registered then
+		lm.connection.bless( main.connection () ):handler ( ibb_incoming_iq_handler, 'iq', 'normal' )
+		main.add_feature ( 'http://jabber.org/protocol/ibb' )
+		ibb_handler_registered = true
+	end
+end
+
+function hook_pre_disconnect ()
+	main.run ( ("color roster * *@%s white"):format ( transport_jid ) )
+	main.run ( ("color roster dn_? *@%s brightblack"):format ( transport_jid ) )
+end
+
+-- Hard hooks, implemented in C
+
+-- hook
+-- - message_in
+--   jid
+--   groupchat
+--   message
+-- - message_out
+--   jid
+--   message
+-- - status_change
+--   jid
+--   resource
+--   new_status
+--   old_status
+--   message
+-- - my_status_change
+--   new_status
+--   message
+function hook_handler ( args )
+	if args.hook == 'message_in' then
+
+		-- beep on ALL messages, no matter, is it chat or something else.
+		if beep_enable then
+			main.beep ()
+		end
+	
+		-- save urls to file from where urlview can get them...
+		for url in args.message:gmatch ( "https?://[%w%p]+" ) do
+			fd = io.open ( url_file, "a" )
+			if fd then
+				fd:write ( url .. "\n" )
+				fd:close ()
+			else
+				print 'Cannot open urls log file'
+			end
+		end
+
+	elseif args.hook == 'status_change' then
+
+		-- delayed actions
+		if delayed_jobs[args.jid] and delayed_jobs[args.jid][args.new_status] then
+			delayed_jobs[args.jid][args.new_status] ()
+			delayed_jobs[args.jid][args.new_status] = nil
+		end
+		
+		-- transported buddies availability indication
+		if args.jid == transport_jid then
+			if args.new_status == '_' then
+				main.run ( ("color roster * *@%s red"):format ( transport_jid ) )
+				main.run ( ("color roster dn_? *@%s red"):format ( transport_jid ) )
+			else
+				main.run ( ("color roster * *@%s white"):format ( transport_jid ) )
+				main.run ( ("color roster dn_? *@%s brightblack"):format ( transport_jid ) )
+			end
+		end
+
+	end
+end
+
+-- (hook_start)
+
+function hook_quit ()
+	save_jobs ()
+
+	-- FIXME
+	if ibb_handler_registered then
+		lm.connection.bless( main.connection () ):handler ( ibb_incoming_iq_handler, 'iq' )
+	end
+end
+
+
+-- The End -- vim: se ts=4: --