irc: Fix error handling

irc: Fix error handling

--- IRC client class
-- @classmod IRCClient

socket = require 'cqueues.socket'
Logger = require 'logger'

escapers =  {['s']: ' ', ['r']: '\r', ['n']: '\n', [';']: ';'}

class IRCClient
	handlers: {}
	senders:  {}
	hooks:    {}
	commands: {}

	default_config: {
		prefix: "!"
	}

	--- Generate a new IRCClient
	-- @tparam string server IRC server name
	-- @tparam number port IRC port number
	-- @tparam table config Default configuration
	new: (server, port=6697, config=@default_config)=>
		assert(server)
		@config = :server, :port, :config, ssl: port == 6697
		for k, v in pairs(config)
			@config[k] = v

		@commands = setmetatable {}, __index: IRCClient.commands
		@hooks    = {}
		@handlers = {}
		@senders  = setmetatable {}, __index: IRCClient.senders
		@server   = {}

	--- Add an IRC bot command
	-- @tparam string name Bot command name
	-- @tparam function command Function for handling command
	add_command: (name, command)=>
		@commands[name] = command

	--- Add a client processing hook
	-- @tparam string id Name of event to hook into
	-- @tparam function hook Processor for hook event
	add_hook: (id, hook)=>
		if not @hooks[id]
			@hooks[id] = {hook}
		else
			table.insert @hooks[id], hook

	--- Add an IRC command handler
	-- @tparam string id IRC command ID (numerics MUST be strings)
	-- @tparam function handler IRC command processor
	add_handler: (id, handler)=>
		if not @handlers[id]
			@handlers[id] = {handler}
		else
			table.insert @handlers[id], handler

	--- Add an IRC command sending handler
	-- @tparam string id IRC command ID ("PRIVMSG", "JOIN", etc.)
	-- @tparam function sender Function to handle message to be sent
	add_sender: (id, sender)=>
		assert not @senders[id], "Sender already exists: " .. id
		@senders[id] = sender

	--- Append modules to the IRCClient based on a table
	-- @tparam table modules Table with senders/handlers/hooks fields
	load_modules: (modules)=>
		if modules.senders
			for id, sender in pairs modules.senders
				@add_sender id, sender
		if modules.handlers
			for id, handler in pairs modules.handlers
				@add_handler id, handler
		if modules.hooks
			for id, hook in pairs modules.hooks
				@add_hook id, hook

	--- Reset all modules in the IRCClient
	clear_modules: ()=>
		@senders = {}
		@handlers = {}
		@hooks = {}
		@commands = {}

	--- Connect to the IRC server specified in the configuration
	connect: ()=>
		if @socket
			Logger.debug "Shutting down socket: #{tostring(@socket)}"
			@socket\shutdown!
		host = @config.server
		port = @config.port
		ssl  = @config.ssl
		debug_msg = ('Connecting... {host: "%s", port: "%s"}')\format host, port
		--
		@config.nick = 'Moon-Moon' if not @config.nick
		@config.username = 'Mooooon' if not @config.username
		@config.realname = 'Moon Moon: MoonScript IRC Bot' if not @config.realname
		--
		Logger.debug debug_msg, Logger.level.warn .. '--- Connecting...'
		@socket = assert socket.connect{:host, :port}
		if ssl
			Logger.debug 'Starting TLS exchange...'
			@socket\starttls!
			Logger.debug 'Started TLS exchange'
		Logger.print Logger.level.okay .. '--- Connected'
		@fire_hook 'CONNECT'
		nick = @config.nick
		user = @config.username
		real = @config.realname
		pass = @config.password
		Logger.print Logger.level.warn .. '--- Sending authentication data'
		@send_raw ('NICK %s')\format nick
		if pass and ssl
			Logger.debug '*** Sending password'
			@send_raw ('PASS :%s')\format pass
		elseif pass
			Logger.print Logger.level.error .. '*** Not sending password: TLS not enabled ***'
		@send_raw ('USER %s * * :%s')\format user, real
		debug_msg = ('Sent authentication data: {nickname: %s, username: %s, realname: %s}')\format nick, user, real
		Logger.debug debug_msg, Logger.level.okay .. '--- Sent authentication data'

	--- Disconnect from the current IRC server
	disconnect: ()=>
		@socket\shutdown! if @socket
		@fire_hook 'DISCONNECT'

	--- Send a raw line to the currently connected IRC server
	-- @param ... List of strings, concatenated using spaces
	send_raw: (...)=>
		@socket\write table.concat({...}, ' ') .. '\n'
		Logger.debug '==> ' .. table.concat {...}, ' '

	--- Send a command using a builtin sender configured with @add_sender
	-- @tparam string name Name of sender to use
	-- @tparam string pattern Pattern to use for formatting
	-- @param ... List of strings to use for formatted string
	-- @see IRCClient\add_sender
	send: (name, pattern, ...)=>
		@send_raw @senders[name] pattern\format ...

	date_pattern: "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).(%d+)Z"

	--- Parse the time of an ISO 8601 compliant string
	-- @tparam string date ISO 8601 compliant YYYY-MM-DDThh:mm:ssZ date
	-- @treturn number Seconds from Epoch from configured date
	parse_time: (date)=>
		year, month, day, hour, min, sec, mil = date\match @date_pattern
		return os.time(:year, :month, :day, :hour, :min, :sec) + tonumber(mil) / 1000

	--- Grab tags from an IRCv3 message
	-- @tparam string tag_message Message containing IRCv3 tags
	-- @treturn table Table containing tags, client tags preceded with a "+"
	parse_tags: (tag_message)=>
		local cur_name
		tags = {}
		charbuf = {}
		pos = 1
		while pos < #tag_message do
			if tag_message\match '^\\', pos
				lookahead = tag_message\sub pos+1, pos+1
				charbuf[#charbuf + 1] = escapers[lookahead] or lookahead
				pos += 2
			elseif cur_name
				if tag_message\match "^;", pos
					tags[cur_name] = table.concat charbuf
					cur_name = nil
					charbuf = {}
					pos += 1
				else
					charbuf[#charbuf + 1], pos = tag_message\match "([^\\;]+)()", pos
			else
				if tag_message\match "^=", pos
					if #charbuf > 0
						cur_name = table.concat charbuf
						charbuf = {}
					pos += 1
				elseif tag_message\match "^;", pos
					if #charbuf > 0
						tags[table.concat charbuf] = true
						charbuf = {}
					pos += 1
				else
					charbuf[#charbuf + 1], pos = tag_message\match "([^\\=;]+)()", pos
		return tags

	--- Parse an IRCv3 compatible wire-format string
	-- @tparam string message_with_tags IRCv3 compliant wire-format command
	-- @treturn table User prefix, if sent by a user or server
	-- @treturn string Command sent to client
	-- @treturn table List of arguments, trailing argument not included
	-- @treturn string Trailing possibly space-inclusive message
	-- @treturn table IRCv3 compatible tag list
	-- @see IRCClient\parse_tags
	parse: (message_with_tags)=>
		local message, tags
		if message_with_tags\sub(1, 1) == '@'
			tags = @parse_tags message_with_tags\sub 2, message_with_tags\find(' ') - 1
			message = message_with_tags\sub((message_with_tags\find(' ') + 1))
		else
			message = message_with_tags
		prefix_end = 0
		prefix = nil
		if message\sub(1, 1) == ':'
			prefix_end = message\find ' '
			prefix = message\sub 2, message\find(' ') - 1

		trailing = nil
		tstart = message\find ' :'
		if tstart
			trailing = message\sub tstart + 2
		else
			tstart = #message

		rest = ((segment)->
			t = {}
			for word in segment\gmatch '%S+'
				table.insert t, word

			return t
		)(message\sub prefix_end + 1, tstart)

		command = rest[1]
		table.remove(rest, 1)
		return prefix, command, rest, trailing, tags

	--- Activate hooks configured for the event name
	-- @tparam string hook_name Name of event to "fire"
	-- @treturn boolean True if no errors were encountered, false otherwise
	-- @treturn table Table containing list of errors from hooks
	fire_hook: (hook_name)=>
		if not @hooks[hook_name] and not IRCClient.hooks[hook_name]
			return false
		has_errors = false
		errors = {}
		if IRCClient.hooks[hook_name] then
			for _, hook in pairs IRCClient.hooks[hook_name]
				Logger.debug Logger.level.warn .. '--- Running global hook: ' .. hook_name
				ok, err = pcall hook, @
				if not ok
					has_errors = true if not has_errors
					table.append(errors, err)
					Logger.print Logger.level.error .. '*** ' .. err
		if @hooks[hook_name] then
			for _, hook in pairs @hooks[hook_name]
				Logger.debug Logger.level.warn .. '--- Running hook: ' .. hook_name
				ok, err = pcall hook, @
				if not ok
					has_errors = true if not has_errors
					table.append(errors, err)
					Logger.print Logger.level.error .. '*** ' .. err
		return has_errors, errors

	--- Run handlers for an unparsed command
	-- @tparam string line Incoming wire-string formatted line from IRC server
	process: (line)=>
		prefix, command, args, rest, tags = @parse line
		Logger.debug Logger.level.warn .. '--- | Line: ' .. line
		Logger.debug Logger.level.okay .. '--- |\\ Running trigger: ' .. Logger.level.warn .. command
		if prefix
			Logger.debug Logger.level.okay .. '--- |\\ Prefix: ' .. prefix
		if #args > 0
			Logger.debug Logger.level.okay .. '--- |\\ Arguments: ' .. table.concat(args, ', ')
		if rest
			Logger.debug Logger.level.okay .. '--- |\\ Trailing: ' .. rest
		if not @handlers[command] and not IRCClient.handlers[command]
			Logger.debug Logger.level.error .. "*** Handler not found for #{command}"
			return
		if IRCClient.handlers[command]
			for _, handler in pairs IRCClient.handlers[command]
				ok, err, tb = xpcall handler, self\log_traceback, self,
					prefix, args, rest, tags
				if not ok
					@log tb
					@log Logger.level.error .. '*** ' .. err
		if @handlers[command]
			for _, handler in pairs @handlers[command]
				ok, err = xpcall handler, (()-> @log_traceback!), @,
					prefix, args, rest, tags
				if not ok
					Logger.print Logger.level.error .. '*** ' .. err

	--- Iterate over lines from a server and handle errors appropriately
	loop: ()=>
		local line
		print_error = (err)->
			Logger.debug "Error: " .. err .. " (" .. line .. ")"

		for received_line in @socket\lines! do
			line = received_line
			xpcall @process, print_error, @, received_line

	--- Print a traceback using the internal logging mechanism
	-- @see IRCClient\log
	log_traceback: (err)=>
		@log Logger.level.error .. '*** ' .. err
		@log debug.traceback()
		@log Logger.level.error .. '---'

	--- Log message from IRC server (used in plugins)
	-- @tparam string input Line to print, IRC color formatted
	-- @see logger
	log: (input)=>
		for line in input\gmatch "[^\r\n]+"
			Logger.print '\00311(\003' .. (@server.caps and
				@server.caps['NETWORK'] or @config.server) ..
				"\00311)\003 #{line}"

return {:IRCClient}

--- IRC client class
-- @classmod IRCClient

socket = require 'cqueues.socket'
Logger = require 'logger'

escapers =  {['s']: ' ', ['r']: '\r', ['n']: '\n', [';']: ';'}

class IRCClient
	handlers: {}
	senders:  {}
	hooks:    {}
	commands: {}

	default_config: {
		prefix: "!"
	}

	--- Generate a new IRCClient
	-- @tparam string server IRC server name
	-- @tparam number port IRC port number
	-- @tparam table config Default configuration
	new: (server, port=6697, config=@default_config)=>
		assert(server)
		@config = :server, :port, :config, ssl: port == 6697
		for k, v in pairs(config)
			@config[k] = v

		@commands = setmetatable {}, __index: IRCClient.commands
		@hooks    = {}
		@handlers = {}
		@senders  = setmetatable {}, __index: IRCClient.senders
		@server   = {}

	--- Add an IRC bot command
	-- @tparam string name Bot command name
	-- @tparam function command Function for handling command
	add_command: (name, command)=>
		@commands[name] = command

	--- Add a client processing hook
	-- @tparam string id Name of event to hook into
	-- @tparam function hook Processor for hook event
	add_hook: (id, hook)=>
		if not @hooks[id]
			@hooks[id] = {hook}
		else
			table.insert @hooks[id], hook

	--- Add an IRC command handler
	-- @tparam string id IRC command ID (numerics MUST be strings)
	-- @tparam function handler IRC command processor
	add_handler: (id, handler)=>
		if not @handlers[id]
			@handlers[id] = {handler}
		else
			table.insert @handlers[id], handler

	--- Add an IRC command sending handler
	-- @tparam string id IRC command ID ("PRIVMSG", "JOIN", etc.)
	-- @tparam function sender Function to handle message to be sent
	add_sender: (id, sender)=>
		assert not @senders[id], "Sender already exists: " .. id
		@senders[id] = sender

	--- Append modules to the IRCClient based on a table
	-- @tparam table modules Table with senders/handlers/hooks fields
	load_modules: (modules)=>
		if modules.senders
			for id, sender in pairs modules.senders
				@add_sender id, sender
		if modules.handlers
			for id, handler in pairs modules.handlers
				@add_handler id, handler
		if modules.hooks
			for id, hook in pairs modules.hooks
				@add_hook id, hook

	--- Reset all modules in the IRCClient
	clear_modules: ()=>
		@senders = {}
		@handlers = {}
		@hooks = {}
		@commands = {}

	--- Connect to the IRC server specified in the configuration
	connect: ()=>
		if @socket
			Logger.debug "Shutting down socket: #{tostring(@socket)}"
			@socket\shutdown!
		host = @config.server
		port = @config.port
		ssl  = @config.ssl
		debug_msg = ('Connecting... {host: "%s", port: "%s"}')\format host, port
		--
		@config.nick = 'Moon-Moon' if not @config.nick
		@config.username = 'Mooooon' if not @config.username
		@config.realname = 'Moon Moon: MoonScript IRC Bot' if not @config.realname
		--
		Logger.debug debug_msg, Logger.level.warn .. '--- Connecting...'
		@socket = assert socket.connect{:host, :port}
		if ssl
			Logger.debug 'Starting TLS exchange...'
			@socket\starttls!
			Logger.debug 'Started TLS exchange'
		Logger.print Logger.level.okay .. '--- Connected'
		@fire_hook 'CONNECT'
		nick = @config.nick
		user = @config.username
		real = @config.realname
		pass = @config.password
		Logger.print Logger.level.warn .. '--- Sending authentication data'
		@send_raw ('NICK %s')\format nick
		if pass and ssl
			Logger.debug '*** Sending password'
			@send_raw ('PASS :%s')\format pass
		elseif pass
			Logger.print Logger.level.error .. '*** Not sending password: TLS not enabled ***'
		@send_raw ('USER %s * * :%s')\format user, real
		debug_msg = ('Sent authentication data: {nickname: %s, username: %s, realname: %s}')\format nick, user, real
		Logger.debug debug_msg, Logger.level.okay .. '--- Sent authentication data'

	--- Disconnect from the current IRC server
	disconnect: ()=>
		@socket\shutdown! if @socket
		@fire_hook 'DISCONNECT'

	--- Send a raw line to the currently connected IRC server
	-- @param ... List of strings, concatenated using spaces
	send_raw: (...)=>
		@socket\write table.concat({...}, ' ') .. '\n'
		Logger.debug '==> ' .. table.concat {...}, ' '

	--- Send a command using a builtin sender configured with @add_sender
	-- @tparam string name Name of sender to use
	-- @tparam string pattern Pattern to use for formatting
	-- @param ... List of strings to use for formatted string
	-- @see IRCClient\add_sender
	send: (name, pattern, ...)=>
		@send_raw @senders[name] pattern\format ...

	date_pattern: "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+).(%d+)Z"

	--- Parse the time of an ISO 8601 compliant string
	-- @tparam string date ISO 8601 compliant YYYY-MM-DDThh:mm:ssZ date
	-- @treturn number Seconds from Epoch from configured date
	parse_time: (date)=>
		year, month, day, hour, min, sec, mil = date\match @date_pattern
		return os.time(:year, :month, :day, :hour, :min, :sec) + tonumber(mil) / 1000

	--- Grab tags from an IRCv3 message
	-- @tparam string tag_message Message containing IRCv3 tags
	-- @treturn table Table containing tags, client tags preceded with a "+"
	parse_tags: (tag_message)=>
		local cur_name
		tags = {}
		charbuf = {}
		pos = 1
		while pos < #tag_message do
			if tag_message\match '^\\', pos
				lookahead = tag_message\sub pos+1, pos+1
				charbuf[#charbuf + 1] = escapers[lookahead] or lookahead
				pos += 2
			elseif cur_name
				if tag_message\match "^;", pos
					tags[cur_name] = table.concat charbuf
					cur_name = nil
					charbuf = {}
					pos += 1
				else
					charbuf[#charbuf + 1], pos = tag_message\match "([^\\;]+)()", pos
			else
				if tag_message\match "^=", pos
					if #charbuf > 0
						cur_name = table.concat charbuf
						charbuf = {}
					pos += 1
				elseif tag_message\match "^;", pos
					if #charbuf > 0
						tags[table.concat charbuf] = true
						charbuf = {}
					pos += 1
				else
					charbuf[#charbuf + 1], pos = tag_message\match "([^\\=;]+)()", pos
		return tags

	--- Parse an IRCv3 compatible wire-format string
	-- @tparam string message_with_tags IRCv3 compliant wire-format command
	-- @treturn table User prefix, if sent by a user or server
	-- @treturn string Command sent to client
	-- @treturn table List of arguments, trailing argument not included
	-- @treturn string Trailing possibly space-inclusive message
	-- @treturn table IRCv3 compatible tag list
	-- @see IRCClient\parse_tags
	parse: (message_with_tags)=>
		local message, tags
		if message_with_tags\sub(1, 1) == '@'
			tags = @parse_tags message_with_tags\sub 2, message_with_tags\find(' ') - 1
			message = message_with_tags\sub((message_with_tags\find(' ') + 1))
		else
			message = message_with_tags
		prefix_end = 0
		prefix = nil
		if message\sub(1, 1) == ':'
			prefix_end = message\find ' '
			prefix = message\sub 2, message\find(' ') - 1

		trailing = nil
		tstart = message\find ' :'
		if tstart
			trailing = message\sub tstart + 2
		else
			tstart = #message

		rest = ((segment)->
			t = {}
			for word in segment\gmatch '%S+'
				table.insert t, word

			return t
		)(message\sub prefix_end + 1, tstart)

		command = rest[1]
		table.remove(rest, 1)
		return prefix, command, rest, trailing, tags

	--- Activate hooks configured for the event name
	-- @tparam string hook_name Name of event to "fire"
	-- @treturn boolean True if no errors were encountered, false otherwise
	-- @treturn table Table containing list of errors from hooks
	fire_hook: (hook_name)=>
		if not @hooks[hook_name] and not IRCClient.hooks[hook_name]
			return false
		has_errors = false
		errors = {}
		if IRCClient.hooks[hook_name] then
			for _, hook in pairs IRCClient.hooks[hook_name]
				Logger.debug Logger.level.warn .. '--- Running global hook: ' .. hook_name
				ok, err = pcall hook, @
				if not ok
					has_errors = true if not has_errors
					table.append(errors, err)
					Logger.print Logger.level.error .. '*** ' .. err
		if @hooks[hook_name] then
			for _, hook in pairs @hooks[hook_name]
				Logger.debug Logger.level.warn .. '--- Running hook: ' .. hook_name
				ok, err = pcall hook, @
				if not ok
					has_errors = true if not has_errors
					table.append(errors, err)
					Logger.print Logger.level.error .. '*** ' .. err
		return has_errors, errors

	--- Run handlers for an unparsed command
	-- @tparam string line Incoming wire-string formatted line from IRC server
	process: (line)=>
		prefix, command, args, rest, tags = @parse line
		Logger.debug Logger.level.warn .. '--- | Line: ' .. line
		Logger.debug Logger.level.okay .. '--- |\\ Running trigger: ' .. Logger.level.warn .. command
		if prefix
			Logger.debug Logger.level.okay .. '--- |\\ Prefix: ' .. prefix
		if #args > 0
			Logger.debug Logger.level.okay .. '--- |\\ Arguments: ' .. table.concat(args, ', ')
		if rest
			Logger.debug Logger.level.okay .. '--- |\\ Trailing: ' .. rest
		if not @handlers[command] and not IRCClient.handlers[command]
			Logger.debug Logger.level.error .. "*** Handler not found for #{command}"
			return
		if IRCClient.handlers[command]
			for _, handler in pairs IRCClient.handlers[command]
				ok, err = xpcall handler, (()-> @log_traceback!), @,
					prefix, args, rest, tags
				if not ok
					Logger.print Logger.level.error .. '*** ' .. err
		if @handlers[command]
			for _, handler in pairs @handlers[command]
				ok, err = xpcall handler, (()-> @log_traceback!), @,
					prefix, args, rest, tags
				if not ok
					Logger.print Logger.level.error .. '*** ' .. err

	--- Iterate over lines from a server and handle errors appropriately
	loop: ()=>
		local line
		print_error =(err)->
			Logger.debug "Error: " .. err .. " (" .. line .. ")"

		for received_line in @socket\lines! do
			line = received_line
			xpcall @process, print_error, @, received_line

	--- Print a traceback using the internal logging mechanism
	-- @see IRCClient\log
	log_traceback: ()->
		@log debug.traceback()

	--- Log message from IRC server (used in plugins)
	-- @tparam string input Line to print, IRC color formatted
	-- @see logger
	log: (input)=>
		prefix = Logger.level[level]
		for line in input\gmatch "[^\r\n]+"
			Logger.print '\00311(\003' .. (@server.caps and
				@server.caps['NETWORK'] or @config.server) ..
				"\00311)\003 #{line}"

return {:IRCClient}


github: prevent "1 more commit not displayed"

Former-commit-id: 644d9efdf1acca33914b6de04b9c97ce3ec2d746 [formerly 45341d58b77fdea560b85719c8d783f2505dfa91]
Former-commit-id: 898ee1b7311dfa2e86530bc7f6dff420681ae13a

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      if i == 1
        message = message .. ' ' .. json.compare
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message}"
    if #json.commits >= 4
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = unescape(destination)
  json = util.json.decode(req.body)

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      if i == 1
        message = message .. ' ' .. json.compare
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message}"
    if #json.commits > 3
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = unescape(destination)
  json = util.json.decode(req.body)

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}


Invalidate bones modifier

Invalidate bones modifier

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

class BonesSequence extends DLib.SequenceBase
	new: (controller, data) =>
		super(controller, data)

		{
			'bones': @bonesNames
			'numid': @numid
		} = data

		@modifierID = controller\GetModifierID(@name .. '_sequence')
		@bonesFuncsPos = ['SetModifier' .. boneName .. 'Position' for boneName in *@bonesNames]
		@bonesFuncsScale = ['SetModifier' .. boneName .. 'Scale' for boneName in *@bonesNames]
		@bonesFuncsAngles = ['SetModifier' .. boneName .. 'Angles' for boneName in *@bonesNames]
		@ent = controller.ent
		@controller = controller
		@Launch()

	GetController: => @controller
	GetEntity: => @ent

	Think: (delta = 0) =>
		@ent = @controller.ent
		return false if not IsValid(@ent)
		super(delta)

	Stop: =>
		super()
		@controller\ResetModifiers(@name .. '_sequence')

	SetBonePosition: (id = 1, val = Vector(0, 0, 0)) => @controller[@bonesFuncsPos[id]] and @controller[@bonesFuncsPos[id]](@controller, @modifierID, val)
	SetBoneScale: (id = 1, val = 0) => @controller[@bonesFuncsScale[id]] and @controller[@bonesFuncsScale[id]](@controller, @modifierID, val)
	SetBoneAngles: (id = 1, val = Angle(0, 0, 0)) => @controller[@bonesFuncsAngles[id]] and @controller[@bonesFuncsAngles[id]](@controller, @modifierID, val)

PPM2.BonesSequence = BonesSequence

-- 0	LrigPelvis
-- 1	Lrig_LEG_BL_Femur
-- 2	Lrig_LEG_BL_Tibia
-- 3	Lrig_LEG_BL_LargeCannon
-- 4	Lrig_LEG_BL_PhalanxPrima
-- 5	Lrig_LEG_BL_RearHoof
-- 6	Lrig_LEG_BR_Femur
-- 7	Lrig_LEG_BR_Tibia
-- 8	Lrig_LEG_BR_LargeCannon
-- 9	Lrig_LEG_BR_PhalanxPrima
-- 10	Lrig_LEG_BR_RearHoof
-- 11	LrigSpine1
-- 12	LrigSpine2
-- 13	LrigRibcage
-- 14	Lrig_LEG_FL_Scapula
-- 15	Lrig_LEG_FL_Humerus
-- 16	Lrig_LEG_FL_Radius
-- 17	Lrig_LEG_FL_Metacarpus
-- 18	Lrig_LEG_FL_PhalangesManus
-- 19	Lrig_LEG_FL_FrontHoof
-- 20	Lrig_LEG_FR_Scapula
-- 21	Lrig_LEG_FR_Humerus
-- 22	Lrig_LEG_FR_Radius
-- 23	Lrig_LEG_FR_Metacarpus
-- 24	Lrig_LEG_FR_PhalangesManus
-- 25	Lrig_LEG_FR_FrontHoof
-- 26	LrigNeck1
-- 27	LrigNeck2
-- 28	LrigNeck3
-- 29	LrigScull
-- 30	Jaw
-- 31	Ear_L
-- 32	Ear_R
-- 33	Mane02
-- 34	Mane03
-- 35	Mane03_tip
-- 36	Mane04
-- 37	Mane05
-- 38	Mane06
-- 39	Mane07
-- 40	Mane01
-- 41	Lrigweaponbone
-- 42	Tail01
-- 43	Tail02
-- 44	Tail03

RESET_BONE_POS = Vector(0, 0, 0)
RESET_BONE_ANGLES = Angle(0, 0, 0)
RESET_BONE_SCALE = Vector(1, 1, 1)
resetBones = (ent) ->
	for i = 0, ent\GetBoneCount() - 1
		ent\ManipulateBonePosition(i, RESET_BONE_POS)
		ent\ManipulateBoneScale(i, RESET_BONE_SCALE)
		ent\ManipulateBoneAngles(i, RESET_BONE_ANGLES)

for ent in *ents.GetAll()
	ent.__ppmBonesModifiers = nil

class PPM2.EntityBonesModifier extends DLib.SequenceHolder
	@OBJECTS = {}
	@resetBones = resetBones

	@SEQUENCES = {
		{
			'name': 'floppy_ears'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -84, -40))
				@SetBoneAngles(2, Angle(0, 84, -40))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'floppy_ears_weak'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -20, -20))
				@SetBoneAngles(2, Angle(0, 20, -20))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'forward_ears'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -15, -27))
				@SetBoneAngles(2, Angle(0, 15, -27))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'neck_flopping_backward'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, -12 * timeOfAnim, math.sin(RealTime() * 4) * 20))
		}

		{
			'name': 'neck_backward'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(0, -12, 0))
		}

		{
			'name': 'neck_twitch'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, math.cos(RealTime() * 4) * 20, 0))
		}

		{
			'name': 'neck_twitch_fast'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, math.cos(RealTime() * 8) * 20, 0))
		}

		{
			'name': 'neck_left'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(14, 0, 12))
		}

		{
			'name': 'neck_right'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(-14, 0, -12))
		}

		{
			'name': 'forward_left'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
				@SetBoneAngles(1, Angle(10, 12, -9))
		}

		{
			'name': 'forward_right'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
				@SetBoneAngles(1, Angle(-10, 12, 9))
		}
	}

	@SequenceObject = BonesSequence

	PreDrawOpaqueRenderables = (a, b) ->
		frame = FrameNumber()
		rtime = RealTime()
		for obj in *@OBJECTS
			if not obj\IsValid()
				oldObjects = @OBJECTS
				@OBJECTS = {}
				for obj2 in *oldObjects
					if obj2\IsValid()
						table.insert(@OBJECTS, obj2)
					else
						lent = obj2.ent
						obj2.invalidate = true
						if IsValid(lent)
							lent.__ppmBonesModifiers = nil
				return

			if obj.callFrame ~= frame and (not obj.pac3Last or obj.pac3Last < rtime) and not obj.ent\IsDormant() and not obj.ent\GetNoDraw()
				resetBones(obj.ent)
				data = obj.ent\GetPonyData()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(obj.ent), data) if data
				obj\Think()

	hook.Add 'PreDrawOpaqueRenderables', 'PPM2.EntityBonesModifier', PreDrawOpaqueRenderables, -5

	new: (ent = NULL) =>
		super()
		@ent = ent
		@bonesMappingID = {}
		@bonesMappingName = {}
		@bonesMapping = {}
		@bonesMappingForName = {}
		@bonesMappingForID = {}
		@bonesIterable = {}
		@boneCount = 0
		@isValid = false
		table.insert(@@OBJECTS, @)
		@lastCall = RealTime()
		@Setup() if IsValid(ent)

	Setup: (ent = @ent) =>
		return false if not IsValid(ent)
		@lastModel = ent\GetModel()
		@ClearModifiers()
		@isValid = true
		@ent = ent
		@bonesMappingID = {}
		@bonesMappingName = {}
		@boneCount = ent\GetBoneCount()
		@bonesIterable = for i = 0, @boneCount - 1
			name = ent\GetBoneName(i)
			@bonesMappingID[i] = name
			@bonesMapping[i] = name
			@bonesMappingName[name] = i
			@bonesMapping[name] = i
			@bonesMappingForName[name] = name
			@bonesMappingForName[i] = name
			@bonesMappingForID[name] = i
			@bonesMappingForID[i] = i
			@RegisterModifier(name .. 'Position', (-> Vector(0, 0, 0)), (-> Vector(0, 0, 0)))
			@RegisterModifier(name .. 'Scale', (-> Vector(0, 0, 0)), (-> Vector(0, 0, 0)))
			@RegisterModifier(name .. 'Angles', (-> Angle(0, 0, 0)), (-> Angle(0, 0, 0)))
			@SetupLerpTables(name .. 'Position')
			@SetupLerpTables(name .. 'Scale')
			@SetupLerpTables(name .. 'Angles')
			@SetLerpFunc(name .. 'Position', LerpVector)
			@SetLerpFunc(name .. 'Scale', LerpVector)
			@SetLerpFunc(name .. 'Angles', LerpAngle)
			--@RegisterModifier(name .. 'Jiggle', 0)
			{i, name, 'Calculate' .. name .. 'Position', 'Calculate' .. name .. 'Scale', 'Calculate' .. name .. 'Angles'}

	Think: (force = false) =>
		return if not super() or not force and @callFrame == FrameNumber()
		@callFrame = FrameNumber()
		for data in *@bonesIterable
			id = data[1]
			name = data[2]
			calc = data[3]
			calcScale = data[4]
			calcAngles = data[5]
			with @ent
				\ManipulateBonePosition(id, \GetManipulateBonePosition(id) + @[calc](@))
				\ManipulateBoneScale(id, \GetManipulateBoneScale(id) + @[calcScale](@))
				\ManipulateBoneAngles(id, \GetManipulateBoneAngles(id) + @[calcAngles](@))

	ResetBones: =>
		return if @pac3Last and @pac3Last > RealTime()
		resetBones(@ent)

	IsValid: => @isValid and @ent\IsValid()
	GetEntity: => @ent

with FindMetaTable('Entity')
	.PPMBonesModifier = =>
		with t = .GetTable(@)
			return if not t
			return .__ppmBonesModifiers if IsValid(.__ppmBonesModifiers) and not .__ppmBonesModifiers.invalidate
			.__ppmBonesModifiers = PPM2.EntityBonesModifier(@)
			.__ppmBonesModifiers.ent = @
			.__ppmBonesModifiers\Setup(@) if .__ppmBonesModifiers.lastModel ~= @GetModel()
			return .__ppmBonesModifiers

hook.Add 'PAC3ResetBones', 'PPM2.EntityBonesModifier', =>
	data = @GetPonyData()
	hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
	if @__ppmBonesModifiers
		@__ppmBonesModifiers\Think()
		@__ppmBonesModifiers.pac3Last = RealTime() + 0.2

ent.__ppmBonesModifiers = nil for ent in *ents.GetAll()

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

class BonesSequence extends DLib.SequenceBase
	new: (controller, data) =>
		super(controller, data)

		{
			'bones': @bonesNames
			'numid': @numid
		} = data

		@modifierID = controller\GetModifierID(@name .. '_sequence')
		@bonesFuncsPos = ['SetModifier' .. boneName .. 'Position' for boneName in *@bonesNames]
		@bonesFuncsScale = ['SetModifier' .. boneName .. 'Scale' for boneName in *@bonesNames]
		@bonesFuncsAngles = ['SetModifier' .. boneName .. 'Angles' for boneName in *@bonesNames]
		@ent = controller.ent
		@controller = controller
		@Launch()

	GetController: => @controller
	GetEntity: => @ent

	Think: (delta = 0) =>
		@ent = @controller.ent
		return false if not IsValid(@ent)
		super(delta)

	Stop: =>
		super()
		@controller\ResetModifiers(@name .. '_sequence')

	SetBonePosition: (id = 1, val = Vector(0, 0, 0)) => @controller[@bonesFuncsPos[id]] and @controller[@bonesFuncsPos[id]](@controller, @modifierID, val)
	SetBoneScale: (id = 1, val = 0) => @controller[@bonesFuncsScale[id]] and @controller[@bonesFuncsScale[id]](@controller, @modifierID, val)
	SetBoneAngles: (id = 1, val = Angle(0, 0, 0)) => @controller[@bonesFuncsAngles[id]] and @controller[@bonesFuncsAngles[id]](@controller, @modifierID, val)

PPM2.BonesSequence = BonesSequence

-- 0	LrigPelvis
-- 1	Lrig_LEG_BL_Femur
-- 2	Lrig_LEG_BL_Tibia
-- 3	Lrig_LEG_BL_LargeCannon
-- 4	Lrig_LEG_BL_PhalanxPrima
-- 5	Lrig_LEG_BL_RearHoof
-- 6	Lrig_LEG_BR_Femur
-- 7	Lrig_LEG_BR_Tibia
-- 8	Lrig_LEG_BR_LargeCannon
-- 9	Lrig_LEG_BR_PhalanxPrima
-- 10	Lrig_LEG_BR_RearHoof
-- 11	LrigSpine1
-- 12	LrigSpine2
-- 13	LrigRibcage
-- 14	Lrig_LEG_FL_Scapula
-- 15	Lrig_LEG_FL_Humerus
-- 16	Lrig_LEG_FL_Radius
-- 17	Lrig_LEG_FL_Metacarpus
-- 18	Lrig_LEG_FL_PhalangesManus
-- 19	Lrig_LEG_FL_FrontHoof
-- 20	Lrig_LEG_FR_Scapula
-- 21	Lrig_LEG_FR_Humerus
-- 22	Lrig_LEG_FR_Radius
-- 23	Lrig_LEG_FR_Metacarpus
-- 24	Lrig_LEG_FR_PhalangesManus
-- 25	Lrig_LEG_FR_FrontHoof
-- 26	LrigNeck1
-- 27	LrigNeck2
-- 28	LrigNeck3
-- 29	LrigScull
-- 30	Jaw
-- 31	Ear_L
-- 32	Ear_R
-- 33	Mane02
-- 34	Mane03
-- 35	Mane03_tip
-- 36	Mane04
-- 37	Mane05
-- 38	Mane06
-- 39	Mane07
-- 40	Mane01
-- 41	Lrigweaponbone
-- 42	Tail01
-- 43	Tail02
-- 44	Tail03

RESET_BONE_POS = Vector(0, 0, 0)
RESET_BONE_ANGLES = Angle(0, 0, 0)
RESET_BONE_SCALE = Vector(1, 1, 1)
resetBones = (ent) ->
	for i = 0, ent\GetBoneCount() - 1
		ent\ManipulateBonePosition(i, RESET_BONE_POS)
		ent\ManipulateBoneScale(i, RESET_BONE_SCALE)
		ent\ManipulateBoneAngles(i, RESET_BONE_ANGLES)

for ent in *ents.GetAll()
	ent.__ppmBonesModifiers = nil

class PPM2.EntityBonesModifier extends DLib.SequenceHolder
	@OBJECTS = {}
	@resetBones = resetBones

	@SEQUENCES = {
		{
			'name': 'floppy_ears'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -84, -40))
				@SetBoneAngles(2, Angle(0, 84, -40))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'floppy_ears_weak'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -20, -20))
				@SetBoneAngles(2, Angle(0, 20, -20))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'forward_ears'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'Ear_L', 'Ear_R'}
			'reset': =>
				@SetBoneAngles(1, Angle(0, -15, -27))
				@SetBoneAngles(2, Angle(0, 15, -27))
			'func': (delta, timeOfAnim) =>
		}

		{
			'name': 'neck_flopping_backward'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, -12 * timeOfAnim, math.sin(RealTime() * 4) * 20))
		}

		{
			'name': 'neck_backward'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(0, -12, 0))
		}

		{
			'name': 'neck_twitch'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, math.cos(RealTime() * 4) * 20, 0))
		}

		{
			'name': 'neck_twitch_fast'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'func': (delta, timeOfAnim) =>
				@SetBoneAngles(1, Angle(0, math.cos(RealTime() * 8) * 20, 0))
		}

		{
			'name': 'neck_left'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(14, 0, 12))
		}

		{
			'name': 'neck_right'
			'autostart': false
			'repeat': false
			'time': 5
			'bones': {'LrigNeck3'}
			'reset': => @SetBoneAngles(1, Angle(-14, 0, -12))
		}

		{
			'name': 'forward_left'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
				@SetBoneAngles(1, Angle(10, 12, -9))
		}

		{
			'name': 'forward_right'
			'autostart': false
			'repeat': false
			'time': 3
			'bones': {'LrigNeck3'}
			'reset': =>
				@SetBoneAngles(1, Angle(-10, 12, 9))
		}
	}

	@SequenceObject = BonesSequence

	PreDrawOpaqueRenderables = (a, b) ->
		frame = FrameNumber()
		rtime = RealTime()
		for obj in *@OBJECTS
			if not obj\IsValid()
				oldObjects = @OBJECTS
				@OBJECTS = {}
				for obj2 in *oldObjects
					if obj2\IsValid()
						table.insert(@OBJECTS, obj2)
					else
						lent = obj2.ent
						if IsValid(lent)
							lent.__ppmBonesModifiers = nil
				return

			if obj.callFrame ~= frame and (not obj.pac3Last or obj.pac3Last < rtime) and not obj.ent\IsDormant() and not obj.ent\GetNoDraw()
				resetBones(obj.ent)
				data = obj.ent\GetPonyData()
				hook.Call('PPM2.SetupBones', nil, StrongEntity(obj.ent), data) if data
				obj\Think()

	hook.Add 'PreDrawOpaqueRenderables', 'PPM2.EntityBonesModifier', PreDrawOpaqueRenderables, -5

	new: (ent = NULL) =>
		super()
		@ent = ent
		@bonesMappingID = {}
		@bonesMappingName = {}
		@bonesMapping = {}
		@bonesMappingForName = {}
		@bonesMappingForID = {}
		@bonesIterable = {}
		@boneCount = 0
		@isValid = false
		table.insert(@@OBJECTS, @)
		@lastCall = RealTime()
		@Setup() if IsValid(ent)

	Setup: (ent = @ent) =>
		return false if not IsValid(ent)
		@lastModel = ent\GetModel()
		@ClearModifiers()
		@isValid = true
		@ent = ent
		@bonesMappingID = {}
		@bonesMappingName = {}
		@boneCount = ent\GetBoneCount()
		@bonesIterable = for i = 0, @boneCount - 1
			name = ent\GetBoneName(i)
			@bonesMappingID[i] = name
			@bonesMapping[i] = name
			@bonesMappingName[name] = i
			@bonesMapping[name] = i
			@bonesMappingForName[name] = name
			@bonesMappingForName[i] = name
			@bonesMappingForID[name] = i
			@bonesMappingForID[i] = i
			@RegisterModifier(name .. 'Position', (-> Vector(0, 0, 0)), (-> Vector(0, 0, 0)))
			@RegisterModifier(name .. 'Scale', (-> Vector(0, 0, 0)), (-> Vector(0, 0, 0)))
			@RegisterModifier(name .. 'Angles', (-> Angle(0, 0, 0)), (-> Angle(0, 0, 0)))
			@SetupLerpTables(name .. 'Position')
			@SetupLerpTables(name .. 'Scale')
			@SetupLerpTables(name .. 'Angles')
			@SetLerpFunc(name .. 'Position', LerpVector)
			@SetLerpFunc(name .. 'Scale', LerpVector)
			@SetLerpFunc(name .. 'Angles', LerpAngle)
			--@RegisterModifier(name .. 'Jiggle', 0)
			{i, name, 'Calculate' .. name .. 'Position', 'Calculate' .. name .. 'Scale', 'Calculate' .. name .. 'Angles'}

	Think: (force = false) =>
		return if not super() or not force and @callFrame == FrameNumber()
		@callFrame = FrameNumber()
		for data in *@bonesIterable
			id = data[1]
			name = data[2]
			calc = data[3]
			calcScale = data[4]
			calcAngles = data[5]
			with @ent
				\ManipulateBonePosition(id, \GetManipulateBonePosition(id) + @[calc](@))
				\ManipulateBoneScale(id, \GetManipulateBoneScale(id) + @[calcScale](@))
				\ManipulateBoneAngles(id, \GetManipulateBoneAngles(id) + @[calcAngles](@))

	ResetBones: =>
		return if @pac3Last and @pac3Last > RealTime()
		resetBones(@ent)

	IsValid: => @isValid and @ent\IsValid()
	GetEntity: => @ent

with FindMetaTable('Entity')
	.PPMBonesModifier = =>
		with t = .GetTable(@)
			return if not t
			return .__ppmBonesModifiers if IsValid(.__ppmBonesModifiers)
			.__ppmBonesModifiers = PPM2.EntityBonesModifier(@)
			.__ppmBonesModifiers.ent = @
			.__ppmBonesModifiers\Setup(@) if .__ppmBonesModifiers.lastModel ~= @GetModel()
			return .__ppmBonesModifiers

hook.Add 'PAC3ResetBones', 'PPM2.EntityBonesModifier', =>
	data = @GetPonyData()
	hook.Call('PPM2.SetupBones', nil, StrongEntity(@), data) if data
	if @__ppmBonesModifiers
		@__ppmBonesModifiers\Think()
		@__ppmBonesModifiers.pac3Last = RealTime() + 0.2

ent.__ppmBonesModifiers = nil for ent in *ents.GetAll()


charset: fix bug with iso. add ascii

charset: fix bug with iso. add ascii

Former-commit-id: 3f6db02c6429ad0ff8c713648d966bef448485be
Former-commit-id: e205c0af863872e435b5269026cc3f91d037e759

iconv = require'iconv'

iso2utf = iconv.new('UTF-8'.."//TRANSLIT", 'ISO-8859-1')
utf2iso = iconv.new('ISO-8859-1'.."//TRANSLIT", 'UTF-8')

toUtf = (s) ->
  iso2utf\iconv(s)

toIso = (s) ->
  utf2iso\iconv(s)

latinpattern = "^#{toIso'æøå'}%??$"

PRIVMSG:
  '^æøå%??$': =>
    reply "UTF-8. Relax, you're doing fine."
  [latinpattern]: =>
    reply 'ISO-8859-1. Pls stahp.'
  '^Ã¦Ã¸Ã¥%??$': =>
    reply 'WTF8. Is very nice.'
  '^fxe%??$': =>
    reply 'fxe is Latin1 with 8th bit stripped'
  '^{%|}%??$': =>
    reply 'du sender NS-4551-1 (aka. ISO-646-NO).. og det er jo litt ut.'
  '^��?%??$': =>
    reply 'du sender replacement chars.'
  '^aeoeaa%?$': =>
    reply 'ASCII!'
  '^���?%??$': =>
    reply 'du sender replacement chars.'
  '^+AOYA+ADl?%??$': =>
    reply 'du sender UTF-7'
  '^%piconv (.+) (.+) (.+)$': (source, dest, frm, to, arg) =>
    convert = iconv.new("#{to\upper!}//TRANSLIT", frm\upper!)
    unless convert
      return say 'Invalid encoding given'
    nstr, err = convert\iconv(arg)
    if err
      return say err

    say nstr

iconv = require'iconv'

iso2utf = iconv.new('UTF-8'.."//TRANSLIT", 'ISO-8859-1')
utf2iso = iconv.new('ISO-8859-1'.."//TRANSLIT", 'UTF-8')

toUtf = (s) ->
  iso2utf\iconv(s)

toIso = (s) ->
  utf2iso\iconv(s)

latinpattern = "^#{toIso'æøå'}%??"

PRIVMSG:
  '^æøå%??$': =>
    reply "UTF-8. Relax, you're doing fine."
  latinpattern: =>
    reply 'ISO-8559-1. Pls stahp.'
  '^Ã¦Ã¸Ã¥%??$': =>
    reply 'WTF8. Is very nice.'
  '^fxe%??$': =>
    reply 'fxe is Latin1 with 8th bit stripped'
  '^{%|}%??$': =>
    reply 'du sender NS-4551-1 (aka. ISO-646-NO).. og det er jo litt ut.'
  '^��?%??$': =>
    reply 'du sender replacement chars.'
  '^���?%??$': =>
    reply 'du sender replacement chars.'
  '^+AOYA+ADl?%??$': =>
    reply 'du sender UTF-7'
  '^%piconv (.+) (.+) (.+)$': (source, dest, frm, to, arg) =>
    convert = iconv.new("#{to\upper!}//TRANSLIT", frm\upper!)
    unless convert
      return say 'Invalid encoding given'
    nstr, err = convert\iconv(arg)
    if err
      return say err

    say nstr


trigger: use namespace triggers instead of triggers

sql = require'lsqlite3'

openDb = ->
  db = sql.open 'cache/trigger.sql'

  db\exec [[
    CREATE TABLE IF NOT EXISTS trigger (
      name UNIQUE ON CONFLICT REPLACE,
      pattern UNIQUE ON CONFLICT REPLACE,
      funcstr
    );
    ]]
  return db

triggerHelp  = (source, destination, argument) =>
   @Msg 'privmsg', destination, source, 'Usage: !trigger add <name>|<lua pattern>|<lua code>. Ex: !trigger add help|^!help (%w+)|say "%s, you need help"'

-- Construct a safe environ for trigger with two functions; say and reply
--- Preferrably you should be able to cross-call modules here to make aliasing possible
sandbox = (func) ->
  (source, destination, ...) =>
    -- Store args so we can use them in env
    arg = {...}
    env =
      say: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      reply: (str) ->
        @Msg 'privmsg', destination, source, source.nick..': '..str, unpack(arg),
      print: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      simplehttp:require'simplehttp'
      json:require'json'
      :string
      :type
      :tostring
      :tonumber
      :ipars
      :pairs
      :table
      :next

    --setfenv func, setmetatable(env, {__index: _G })
    setfenv func, env
    success, err = pcall func, arg
    if err
      @Msg 'privmsg', destination, source, err
    else
      success

-- Construct a safe function to run and return a handler
triggerHandler = (source, destination, funcstr) =>
  func, err = loadstring funcstr
  unless func
    @Msg 'privmsg', destination, source, 'Trigger error: %s', err
  else
    sandbox(func)

-- Register the command
regCommand = (source, destination, name, pattern, funcstr) =>
  db = openDb!
  ins = db\prepare "INSERT INTO trigger (name, pattern, funcstr) VALUES(?, ?, ?)"
  code = ins\bind_values name, pattern, funcstr
  code = ins\step!
  code = ins\finalize!
  db\close!

  @RegisterCommand 'trigger', pattern, triggerHandler(@, source, destination, funcstr)

delCommand = (source, destination, name) =>
  db = openDb!
  -- Find pattern which is used for deletion of handler
  stmt = db\prepare "SELECT pattern FROM trigger WHERE name = ?"
  code = stmt\bind_values name
  code = stmt\step!
  if code == sqlite3.DONE
    -- Invalid name
    db\close!
    return
  pattern = stmt\get_values()[1]

  -- Delete trigger from db
  ins = db\prepare "DELETE FROM trigger WHERE name = ?"
  code = ins\bind_values pattern
  code = ins\step!
  code = ins\finalize!
  db\close!

  @UnregisterCommand 'triggers', pattern

-- Register commands on startup
db = openDb!
for row in db\nrows 'SELECT pattern, funcstr FROM trigger'
  ivar2\RegisterCommand 'triggers', row.pattern, triggerHandler(ivar2, nil, nil, row.funcstr)
db\close!

PRIVMSG:
  '%ptrigger$': triggerHelp
  '%ptrigger add (.+)|(.+)|(.+)$': regCommand
  '%ptrigger del (.+)$': delCommand

sql = require'lsqlite3'

openDb = ->
  db = sql.open 'cache/trigger.sql'

  db\exec [[
    CREATE TABLE IF NOT EXISTS trigger (
      name UNIQUE ON CONFLICT REPLACE,
      pattern UNIQUE ON CONFLICT REPLACE,
      funcstr
    );
    ]]
  return db

triggerHelp  = (source, destination, argument) =>
   @Msg 'privmsg', destination, source, 'Usage: !trigger add <name>|<lua pattern>|<lua code>. Ex: !trigger add help|^!help (%w+)|say "%s, you need help"'

-- Construct a safe environ for trigger with two functions; say and reply
--- Preferrably you should be able to cross-call modules here to make aliasing possible
sandbox = (func) ->
  (source, destination, ...) =>
    -- Store args so we can use them in env
    arg = {...}
    env =
      say: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      reply: (str) ->
        @Msg 'privmsg', destination, source, source.nick..': '..str, unpack(arg),
      print: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      simplehttp:require'simplehttp'
      json:require'json'
      :string
      :type
      :tostring
      :tonumber
      :ipars
      :pairs
      :table
      :next

    --setfenv func, setmetatable(env, {__index: _G })
    setfenv func, env
    success, err = pcall func, arg
    if err
      @Msg 'privmsg', destination, source, err
    else
      success

-- Construct a safe function to run and return a handler
triggerHandler = (source, destination, funcstr) =>
  func, err = loadstring funcstr
  unless func
    @Msg 'privmsg', destination, source, 'Trigger error: %s', err
  else
    sandbox(func)

-- Register the command
regCommand = (source, destination, name, pattern, funcstr) =>
  db = openDb!
  ins = db\prepare "INSERT INTO trigger (name, pattern, funcstr) VALUES(?, ?, ?)"
  code = ins\bind_values name, pattern, funcstr
  code = ins\step!
  code = ins\finalize!
  db\close!

  @RegisterCommand 'trigger', pattern, triggerHandler(@, source, destination, funcstr)

delCommand = (source, destination, name) =>
  db = openDb!
  -- Find pattern which is used for deletion of handler
  stmt = db\prepare "SELECT pattern FROM trigger WHERE name = ?"
  code = stmt\bind_values name
  code = stmt\step!
  if code == sqlite3.DONE
    -- Invalid name
    db\close!
    return
  pattern = stmt\get_values()[1]

  -- Delete trigger from db
  ins = db\prepare "DELETE FROM trigger WHERE name = ?"
  code = ins\bind_values pattern
  code = ins\step!
  code = ins\finalize!
  db\close!

  @UnregisterCommand 'trigger', pattern

-- Register commands on startup
db = openDb!
for row in db\nrows 'SELECT pattern, funcstr FROM trigger'
  ivar2\RegisterCommand 'trigger', row.pattern, triggerHandler(ivar2, nil, nil, row.funcstr)
db\close!

PRIVMSG:
  '%ptrigger$': triggerHelp
  '%ptrigger add (.+)|(.+)|(.+)$': regCommand
  '%ptrigger del (.+)$': delCommand


Register the current directory as a project

import app, signal, Project from howl
import File from howl.io

tstack = {}

handler = (args) ->
  tstack[#tstack+1] = args.file
  if args.file.basename == '-'
    app.window\remove_view!
    fn = tstack[#tstack-1] or File '.howl-proj'
    file = fn\open!
    app\open_file File '.howl-proj' unless tstack[#tstack-1]
    table.insert Project.roots, File '.'
    script = ''
    iscript = false
    for line in file\lines!
      continue if line\gsub("^%s*(.-)%s*$", "%1") == ''
      if iscript
        script ..= line .. '\n'
      elseif line == '!:'
        iscript = true
      else
        app\open_file File line
        app.window\remove_view! if #app.window.views > 1
    app\close_buffer args.buffer
    if script != ''
      f = require('moonscript').loadstring script
      assert f
      f!

signal.connect 'file-opened', handler

{
  info:
    author: 'Ryan Gonzalez'
    description: 'An extended project system'
    license: 'MIT'
  unload: ->
    signal.disconnect 'file-opened', handler
}

import app, signal from howl
import File from howl.io

tstack = {}

handler = (args) ->
  tstack[#tstack+1] = args.file
  if args.file.basename == '-'
    app.window\remove_view!
    fn = tstack[#tstack-1] or File '.howl-proj'
    file = fn\open!
    app\open_file File '.howl-proj' unless tstack[#tstack-1]
    script = ''
    iscript = false
    for line in file\lines!
      continue if line\gsub("^%s*(.-)%s*$", "%1") == ''
      if iscript
        script ..= line .. '\n'
      elseif line == '!:'
        iscript = true
      else
        app\open_file File line
        app.window\remove_view! if #app.window.views > 1
    app\close_buffer args.buffer
    if script != ''
      f = require('moonscript').loadstring script
      assert f
      f!

signal.connect 'file-opened', handler

{
  info:
    author: 'Ryan Gonzalez'
    description: 'An extended project system'
    license: 'MIT'
  unload: ->
    signal.disconnect 'file-opened', handler
}


main: Fix detection of data

main: Fix detection of data

#!/usr/bin/env moon
--- Create and manage IRC bots.
-- @script main.moon
-- @author Ryan "ChickenNuggers" <ryan@hashbang.sh>

Logger        = require 'logger' -- vim:set noet sts=0 sw=3 ts=3:
cqueues       = require 'cqueues'
lfs           = require 'lfs'

import IRCClient from require 'irc'

wd = lfs.currentdir()
Logger.set_debug true if os.getenv 'DEBUG'

pcall -> -- use StackTracePlus if available; it's much better than builtin
	debug.traceback = require("StackTracePlus").stacktrace

load_modules = (folder)->
	for file in lfs.dir folder
		if file\match "%.lua$"
			dofile folder .. '/' .. file

load_modules_in_plugin_folders = ->
	for module_folder in *{'plugins', 'modules'}
		full_path = wd .. '/' .. module_folder
		load_modules full_path if lfs.attributes(full_path, 'mode') == 'directory'

load_modules_in_plugin_folders!

bots = {}
conf_home = os.getenv('XDG_CONFIG_HOME') or os.getenv('HOME') .. '/.config'
for file in lfs.dir "#{conf_home}/moonmoon"
	if file\match "%.lua$"
		local data
		(loadfile "#{conf_home}/moonmoon/#{file}", nil, {
			bot: (name)->
				return (file_data)->
					file_data.name = name
					file_data.file = file
					data = file_data
		})()
		unless data and data.server
			print "Missing `server` field: [#{file}]"
			continue
		if os.getenv 'DEBUG'
			for key, value in pairs data
				if type(value) == "string" then
					print ("%q: %q")\format key, value
				else
					print ("%q: %s")\format key, value
		bot = IRCClient data.server, data.port, data
		table.insert(bots, bot)

queue = cqueues.new!

package.loaded["queue"] = queue -- for use in modules

for bot in *bots
	queue\wrap ->
		while true
			local success
			for i=1, 3 do -- three tries
				ok, err = pcall bot.connect, bot
				success = ok
				if not ok
					Logger.print Logger.level.error .. '*** Unable to connect: ' .. bot.config.server
					Logger.debug Logger.level.error .. '*** ' .. err
				else
					break
			if not success
				Logger.print Logger.level.fatal .. '*** Not connecting anymore for: ' .. bot.config.file
				return

			ok, err = pcall -> bot\loop!
			if not ok then
				Logger.print Logger.level.error .. err

assert queue\loop!

#!/usr/bin/env moon
--- Create and manage IRC bots.
-- @script main.moon
-- @author Ryan "ChickenNuggers" <ryan@hashbang.sh>

Logger        = require 'logger' -- vim:set noet sts=0 sw=3 ts=3:
cqueues       = require 'cqueues'
lfs           = require 'lfs'

import IRCClient from require 'irc'

wd = lfs.currentdir()
Logger.set_debug true if os.getenv 'DEBUG'

pcall -> -- use StackTracePlus if available; it's much better than builtin
	debug.traceback = require("StackTracePlus").stacktrace

load_modules = (folder)->
	for file in lfs.dir folder
		if file\match "%.lua$"
			dofile folder .. '/' .. file

load_modules_in_plugin_folders = ->
	for module_folder in *{'plugins', 'modules'}
		full_path = wd .. '/' .. module_folder
		load_modules full_path if lfs.attributes(full_path, 'mode') == 'directory'

load_modules_in_plugin_folders!

bots = {}
conf_home = os.getenv('XDG_CONFIG_HOME') or os.getenv('HOME') .. '/.config'
for file in lfs.dir "#{conf_home}/moonmoon"
	if file\match "%.lua$"
		local data
		(loadfile "#{conf_home}/moonmoon/#{file}", nil, {
			bot: (name)->
				return (file_data)->
					file_data.name = name
					file_data.file = file
					data = file_data
		})()
		assert data.server, "Missing `server` field: [#{file}]"
		if os.getenv 'DEBUG'
			for key, value in pairs data
				if type(value) == "string" then
					print ("%q: %q")\format key, value
				else
					print ("%q: %s")\format key, value
		bot = IRCClient data.server, data.port, data
		table.insert(bots, bot)

queue = cqueues.new!

package.loaded["queue"] = queue -- for use in modules

for bot in *bots
	queue\wrap ->
		while true
			local success
			for i=1, 3 do -- three tries
				ok, err = pcall bot.connect, bot
				success = ok
				if not ok
					Logger.print Logger.level.error .. '*** Unable to connect: ' .. bot.data.host
					Logger.debug Logger.level.error .. '*** ' .. err
				else
					break
			if not success
				Logger.print Logger.level.fatal .. '*** Not connecting anymore for: ' .. bot.config_file
				return

			ok, err = pcall -> bot\loop!
			if not ok then
				Logger.print Logger.level.error .. err

assert queue\loop!


improved user management enabler

improved user management enabler

Users=require "lazuli.modules.user_management.models.users"
import dump from require "moonscript.util"

wasLoaded=false

=>
  @include "lazuli.modules.user_management" if not wasLoaded
  wasLoaded=true
  @before_filter =>
    @modules.user_management or={}
    @session.modules.user_management or={}
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser

Users=require "lazuli.modules.user_management.models.users"
import dump from require "moonscript.util"

=>
  f=io.open "/tmp/a","w"
  f\write dump @
  f\close!
  @@modules or={}
  @include "lazuli.modules.user_management" if not @modules.user_management
  @@modules.user_management or={}
  @before_filter =>
    @session.modules.user_management or={}
    if @session.modules.user_management.currentuser and not @modules.user_management.currentuser
      @modules.user_management.currentuser = Users\find @session.modules.user_management.currentuser


Added documentation and halfWidt/height to Entity

require "LovePunk.LP"
require "LovePunk.Mask"
require "LovePunk.Tweener"

export ^

class Entity extends Tweener
	new: (x = 0, y = 0, graphic = nil, mask = nil) =>
		super!
		-- If the Entity should render.
		@visible = true
		-- If the Entity should respond to collision checks.
		@collidable = true
		-- If the entity should follow the camera.
		@followCamera = false
		-- X position of the Entity in the Scene.
		@__x = x
		-- Y position of the Entity in the Scene.
		@__y = y
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- Width of the Entity's hitbox.
		@width = 0
		-- Height of the Entity's hitbox.
		@height = 0

		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		@__moveX, @__moveY = 0, 0
		-- The collision type, used for collision checking.
		@type = ""
		@name = ""

		@__hitbox = Mask!
		@__point = LP.point
		@__camera = LP.point2

		-- The rendering layer of this Entity. Higher layers are rendered first.
		@layer = LP.BASELAYER!

		-- Graphical component to render to the screen.
		@__graphic = graphic if graphic != nil

		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@__mask = mask if mask != nil

		@__hitbox\assignTo @

	-- X position of the Entity in the Scene.
	x: (value) =>
		if value != nil
			@__x = value
			return @__x

		if @followCamera
			@__x + LP.camera.x
		else
			@__x

	-- Y position of the Entity in the Scene.
	y: (value) =>
		if value != nil
			@__y = value
			return @__y
		if @followCamera
			@__y + LP.camera.y
		else
			@__y

	halfWidth: => @width/2
	halfHeight: => @height/2

	-- The World object is deprecated for FlashPunk-like compatibility
	world: (value) =>
		@scene = value if value != nil
		@scene

	-- Override this, called when the Entity is added to a Scene.
	added: =>
	-- Override this, called when the Entity is removed from a Scene.
	removed: =>
	-- Updates the Entity.
	update: =>

	-- Renders the Entity. If you override this for special behaviour,
	-- remember to call super.render() to render the Entity's graphic.
	render: =>
		if @__graphic != nil and @__graphic.visable
			if @__graphic.relative
				@__point.x = @x!
				@__point.y = @y!
			else
				@__point.x, @__point.y = 0, 0
				@__camera.x = if @__scene == nil then LP.camera.x else @__scene.camera.x
				@__camera.y = if @__scene == nil then LP.camera.y else @__scene.camera.y
				@__graphic\render (if @__renderTarget != nil then @__renderTarget else LP.buffer), @__point, @__camera

	collide: (type, x, y) =>

	-- Checks for collision against multiple Entity types.
	-- @param	types		An Array or Vector of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @return	The first Entity collided with, or null if none were collided.
	collideTypes: (types, x, y) =>

	-- Checks if this Entity collides with a specific Entity.
	-- @param	e		The Entity to collide against.
	-- @param	x		Virtual x position to place this Entity.
	-- @param	y		Virtual y position to place this Entity.
	-- @return	The Entity if they overlap, or null if they don't.
	collideWith: (e, x, y) =>

	-- Checks if this Entity overlaps the specified rectangle.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	rX			X position of the rectangle.
	-- @param	rY			Y position of the rectangle.
	-- @param	rWidth		Width of the rectangle.
	-- @param	rHeight		Height of the rectangle.
	-- @return	If they overlap.
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>

	-- Checks if this Entity overlaps the specified position.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	pX			X position.
	-- @param	pY			Y position.
	-- @return	If the Entity intersects with the position.
	collidePoint: (x, y, pX, pY) =>

	-- Populates an array with all collided Entities of a type.
	-- @param	type		The Entity type to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideInto: (type, x, y, array) =>

	-- Populates an array with all collided Entities of multiple types.
	-- @param	types		An array of Entity types to check for.
	-- @param	x			Virtual x position to place this Entity.
	-- @param	y			Virtual y position to place this Entity.
	-- @param	array		The Array or Vector object to populate.
	-- @return	The array, populated with all collided Entities.
	collideTypesInto: (types, x, y, array) =>

	-- Adds the graphic to the Entity via a Graphiclist.
	-- @param	g		Graphic to add.
	addGraphic: (g) =>

	-- Sets the Entity's hitbox properties.
	-- @param	width		Width of the hitbox.
	-- @param	height		Height of the hitbox.
	-- @param	originX		X origin of the hitbox.
	-- @param	originY		Y origin of the hitbox.
	setHitbox: (width, height, originX, originY) =>
		@width, @height = width, height
		@originX, @originY = originX, originY

	-- Sets the Entity's hitbox to match that of the provided object.
	-- @param	o		The object defining the hitbox (eg. an Image or Rectangle).
	setHitboxTo: (o) =>

	-- Sets the origin of the Entity.
	-- @param	x		X origin.
	-- @param	y		Y origin.
	setOrigin: (x, y) =>

	-- Center's the Entity's origin (half width & height).
	centerOrigin: =>

	-- Calculates the distance from another Entity.
	-- @param	e				The other Entity.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceFrom: (e, useHitboxes) =>

	-- Calculates the distance from this Entity to the point.
	-- @param	px				X position.
	-- @param	py				Y position.
	-- @param	useHitboxes		If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used.
	-- @return	The distance.
	distanceToPoint: (px, py, useHitbox) =>

	-- Calculates the distance from this Entity to the rectangle.
	-- @param	rx			X position of the rectangle.
	-- @param	ry			Y position of the rectangle.
	-- @param	rwidth		Width of the rectangle.
	-- @param	rheight		Height of the rectangle.
	-- @return	The distance.
	distanceToRect: (rx, ry, rwidth, rheight) =>

	-- Gets the class name as a string.
	-- @return	A string representing the class name.
	toString: =>

	__tostring: => @toString!

	-- Moves the Entity by the amount, retaining integer values for its x and y.
	-- @param	x			Horizontal offset.
	-- @param	y			Vertical offset.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveBy: (x, y, solidType, sweep) =>

	-- Moves the Entity to the position, retaining integer values for its x and y.
	-- @param	x			X position.
	-- @param	y			Y position.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTo: (x, y, solidType, sweep) =>

	-- Moves towards the target position, retaining integer values for its x and y.
	-- @param	x			X target.
	-- @param	y			Y target.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveTowards: (x, y, amount, solidType, sweep) =>

	-- Moves at an angle by a certain amount, retaining integer values for its x and y.
	-- @param	angle		Angle to move at in degrees.
	-- @param	amount		Amount to move.
	-- @param	solidType	An optional collision type to stop flush against upon collision.
	-- @param	sweep		If sweeping should be used (prevents fast-moving objects from going through solidType).
	moveAtAngle: (angle, amount, solidType, sweep) =>

	-- When you collide with an Entity on the x-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideX: (e) =>

	-- When you collide with an Entity on the y-axis with moveTo() or moveBy().
	-- @param	e		The Entity you collided with.
	moveCollideY: (e) =>

	-- Clamps the Entity's hitbox on the x-axis.
	-- @param	left		Left bounds.
	-- @param	right		Right bounds.
	-- @param	padding		Optional padding on the clamp.
	clampHorizontal: (left, right, padding) =>

	-- Clamps the Entity's hitbox on the y axis.
	-- @param	top			Min bounds.
	-- @param	bottom		Max bounds.
	-- @param	padding		Optional padding on the clamp.
	clampVertical: (top, bottom, padding) =>

	-- Center graphic inside bounding rect.
	centerGraphicInRect: =>

require "LovePunk.LP"
require "LovePunk.Mask"
require "LovePunk.Tweener"

export ^

class Entity extends Tweener
	new: (x = 0, y = 0, graphic = nil, mask = nil) =>
		super!
		-- If the Entity should render.
		@visible = true
		-- If the Entity should respond to collision checks.
		@collidable = true
		-- If the entity should follow the camera.
		@followCamera = false
		-- X position of the Entity in the Scene.
		@__x = x
		-- Y position of the Entity in the Scene.
		@__y = y
		-- X origin of the Entity's hitbox.
		@originX = 0
		-- Y origin of the Entity's hitbox.
		@originY = 0
		-- Width of the Entity's hitbox.
		@width = 0
		-- Height of the Entity's hitbox.
		@height = 0

		-- The bottommost position of the Entity's hitbox.
		@bottom = 0
		-- The center x position of the Entity's hitbox.
		@centerX = 0
		-- The center y position of the Entity's hitbox.
		@centerY = 0
		@__moveX, @__moveY = 0, 0
		-- The collision type, used for collision checking.
		@type = ""
		@name = ""

		@__hitbox = Mask!
		@__point = LP.point
		@__camera = LP.point2

		-- The rendering layer of this Entity. Higher layers are rendered first.
		@layer = LP.BASELAYER!

		-- Graphical component to render to the screen.
		@__graphic = graphic if graphic != nil

		-- An optional Mask component, used for specialized collision. If this is not assigned, collision checks will use the Entity's hitbox by default.
		@__mask = mask if mask != nil

		@__hitbox\assignTo @

	x: (value) =>
		if value != nil
			@__x = value
			return @__x

		if @followCamera
			@__x + LP.camera.x
		else
			@__x

	y: (value) =>
		if value != nil
			@__y = value
			return @__y
		if @followCamera
			@__y + LP.camera.y
		else
			@__y

	-- The World object is deprecated for FlashPunk-like compatibility
	world: (value) =>
		@scene = value if value != nil
		@scene

	-- Override this, called when the Entity is added to a Scene.
	added: =>
	-- Override this, called when the Entity is removed from a Scene.
	removed: =>
	-- Updates the Entity.
	update: =>
	-- Renders the Entity. If you override this for special behaviour,
	-- remember to call super.render() to render the Entity's graphic.
	render: =>
		if @__graphic != nil and @__graphic.visable
			if @__graphic.relative
				@__point.x = @x!
				@__point.y = @y!
			else
				@__point.x, @__point.y = 0, 0
				@__camera.x = if @__scene == nil then LP.camera.x else @__scene.camera.x
				@__camera.y = if @__scene == nil then LP.camera.y else @__scene.camera.y
				@__graphic\render (if @__renderTarget != nil then @__renderTarget else LP.buffer), @__point, @__camera

	collide: (type, x, y) =>
	collideTypes: (types, x, y) =>
	collideWith: (e, x, y) =>
	collideRect: (x, y, rX, rY, rWidth, rHeight) =>
	collidePoint: (x, y, pX, pY) =>
	collideInto: (type, x, y, array) =>
	collideTypesInto: (types, x, y, array) =>
	addGraphic: (g) =>

	setHitbox: (width, height, originX, originY) =>
		@width, @height = width, height
		@originX, @originY = originX, originY

	setHitboxTo: (o) =>
	setOrigin: (x, y) =>
	centerOrigin: =>
	distanceFrom: (e, useHitboxes) =>
	distanceToPoint: (px, py, useHitbox) =>
	distanceToRect: (rx, ry, rwidth, rheight) =>
	toString: =>
	moveBy: (x, y, solidType, sweep) =>
	moveTo: (x, y, solidType, sweep) =>
	moveTowards: (x, y, amount, solidType, sweep) =>
	moveAtAngle: (angle, amount, solidType, sweep) =>
	moveCollideX: (e) =>
	moveCollideY: (e) =>
	clampHorizontal: (left, right, padding) =>
	clampVertical: (top, bottom, padding) =>
	centerGraphicInRect: =>


Removed useless parens

Removed useless parens

require "LunoPunk.Entity"
require "LunoPunk.RenderMode"
require "LunoPunk.Screen"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.utils.Draw"
require "LunoPunk.utils.Event"
require "LunoPunk.utils.EventListener"
require "LunoPunk.utils.Input"

export ^

-- The main class which manages the game loop
class Engine
	-- Constructor. Defines startup information about your game.
	-- @param width      The width of your game.
	-- @param height     The height of your game.
	-- @param frameRate  The game framerate, in frames per second.
	-- @param fixed      If a fixed-framerate should be used.
	-- @param renderMode Overrides the default render mode for this target
	new: (width = 0, height = 0, frameRate = 0, fixed = false, renderMode) =>
		LP.bounds = Rectangle 0, 0, width, height
		LP.assignedFrameRate = frameRate
		LP.fixed = fixed

		LP.engine = @
		LP.screen = Screen!
		LP.width = width
		LP.height = height

		LP.randomizeSeed! unless LP.randomSeed

		LP.entity = Entity!
		LP.time = love.timer.getTime!

		-- Cap on the elapsed time (default at 30 FPS). Raise this to allow for lower framerates (eg. 1 / 10).
		@maxElapsed = 0.0333
		-- The max amount of frames that can be skipped in fixed framerate mode.
		@maxFrameSkip = 0
		-- If the game should stop updating/rendering.
		@paused = false
		-- The amount of milliseconds between ticks in fixed framerate mode.
		@tickRate = 4

		@__frameList = {}
		@__frameLast = 0
		@__frameListSum = 0

		-- Do something with the Löve functions...
		love.draw = @\render
		love.update = @\update
		love.focus = (f) ->
			if f
				DispatchEvent Event Event.ACTIVATE
-- 				@focusGained!
			else
				DispatchEvent Event Event.DEACTIVATE
-- 				@focusLost!

		if love._version_major >= 0 and love._version_minor >= 9
			love.resize = (w, h) -> DispatchEvent Event(Event.RESIZE), w, h

		-- call some more setup from here
		setup @

	-- Override this, called after Engine has been added to the stage.
	init: =>

	-- Override this, called when game gains focus
	focusGained: =>

	-- Override this, called when game loses focus
	focusLost: =>

	-- Updates the game, updating the Scene and Entities.
	update: =>
		LP.scene\updateLists!
		checkScene! if LP\sceneIsNull!
		LP.tweener\updateTweens! if LP.tweener.active and LP.tweener\hasTween!

		if LP.scene.active
			LP.scene\updateTweens! if LP.scene\hasTween!
			LP.scene\update!

		LP.scene\updateLists false

	-- Renders the game, rendering the Scene and Entities.
	render: =>
		if LP.screen.needsResize!
			LP.resize LP.windowWidth, LP.windowHeight
			LP.screen.needsResize = => false

		-- timing stuff
		t = love.timer.getDelta!

		@__frameLast = t if @__frameLast == 0

		-- render loop
		if LP.renderMode == RenderMode.Buffer
			LP.screen\swap!
			LP.screen\refresh!

		Draw.resetTarget!

		LP.scene\render! if LP.scene.visible

		LP.screen\redraw! if LP.renderMode == RenderMode.Buffer

		-- more timionStageng stuff
		t = love.timer.getTime!
		@__frameList[#@__frameList] = t - @__frameLast
		@__frameListSum += @__frameList[#@__frameList]
		if #@__frameList > 10
			@__frameListSum -= @__frameList[#@__frameList]
			@__frameList = [frame for frame in *@__frameList[2,]]

		LP.frameRate = 1000 / (@__frameListSum / #@__frameList)
		@__frameLast = t

	-- Sets the game's graphics properties. Override this to set them differently.
	setGraphicsProperties: =>
		if love._version_major >= 0 and love._version_minor >= 9
			-- love.window is Löve >= 0.9
			LP.windowWidth = love.window.getWidth!
			LP.windowHeight = love.window.getHeight!
		else
			LP.windowWidth = love.graphics.getWidth!
			LP.windowHeionStageght = love.graphics.getHeight!

		@resize! -- call resize once to initialize the screen

		-- set resize event
		AddEventListener Event.RESIZE, (e) -> @resize!

		AddEventListener Event.ACTIVATE, (e) ->
			LP.focused = true
			@focusGained!
			LP.scene\focusGained!

		AddEventListener Event.DEACTIVATE, (e) ->
			LP.focused = false
			@focusLost!
			LP.scene\focusLost!

	-- @private Event handler for window resize
	resize: =>
		LP.width = love.graphics.getWidth! if LP.width == 0
		LP.height = love.graphics.getHeight! if LP.height == 0

		-- calculate scale from width/height values
		if love._version_major >= 0 and love._version_minor >= 9
			-- love.window is Löve >= 0.9
			LP.windowWidth = love.window.getWidth!
			LP.windowHeight = love.window.getHeight!
		else
			LP.windowWidth = love.graphics.getWidth!
			LP.windowHeight = love.graphics.getHeight!

		LP.screen\scaleX love.graphics.getWidth! / LP.width
		LP.screen\scaleY love.graphics.getHeight! / LP.height
		LP.resize love.graphics.getWidth!, love.graphics.getHeight!

	-- @private Finish the setup
	-- HaxePunk this is onStage, but there isn't any async in the setup with Löve
	setup = =>
		@setGraphicsProperties!

		-- enable input
		Input.enable!

		-- switch scenes
		@checkScene! if not LP\sceneIsNull!

		-- game start
		@init!

	-- @private Framerate independent game loop.
	onEnterFrame = (e) =>

	-- @private Switch scenes if they've changed.
	checkScene = =>
		return if not LP\sceneIsNull!

		if LP.scene != nil
			LP.scene\endScene!
			LP.scene\updateLists!
			LP.scene\clearTweens! if LP.scene.autoClear and LP.scene\hasTween!
			-- TODO
			-- removeChild(LP.scene.sprite) if LP.scene.sprite
			LP\swapScene!
			-- TODO
			-- addChild LP.scene.sprite
			LP.camera = LP.scene.camera
			LP.scene\begin!
			LP.scene\updateLists!

require "LunoPunk.Entity"
require "LunoPunk.RenderMode"
require "LunoPunk.Screen"
require "LunoPunk.geometry.Rectangle"
require "LunoPunk.utils.Draw"
require "LunoPunk.utils.Event"
require "LunoPunk.utils.EventListener"
require "LunoPunk.utils.Input"

export ^

-- The main class which manages the game loop
class Engine
	-- Constructor. Defines startup information about your game.
	-- @param width      The width of your game.
	-- @param height     The height of your game.
	-- @param frameRate  The game framerate, in frames per second.
	-- @param fixed      If a fixed-framerate should be used.
	-- @param renderMode Overrides the default render mode for this target
	new: (width = 0, height = 0, frameRate = 0, fixed = false, renderMode) =>
		LP.bounds = Rectangle(0, 0, width, height)
		LP.assignedFrameRate = frameRate
		LP.fixed = fixed

		LP.engine = @
		LP.screen = Screen!
		LP.width = width
		LP.height = height

		LP.randomizeSeed! unless LP.randomSeed

		LP.entity = Entity!
		LP.time = love.timer.getTime!

		-- Cap on the elapsed time (default at 30 FPS). Raise this to allow for lower framerates (eg. 1 / 10).
		@maxElapsed = 0.0333
		-- The max amount of frames that can be skipped in fixed framerate mode.
		@maxFrameSkip = 0
		-- If the game should stop updating/rendering.
		@paused = false
		-- The amount of milliseconds between ticks in fixed framerate mode.
		@tickRate = 4

		@__frameList = {}
		@__frameLast = 0
		@__frameListSum = 0

		-- Do something with the Löve functions...
		love.draw = @\render
		love.update = @\update
		love.focus = (f) ->
			if f
				DispatchEvent Event Event.ACTIVATE
-- 				@focusGained!
			else
				DispatchEvent Event Event.DEACTIVATE
-- 				@focusLost!

		if love._version_major >= 0 and love._version_minor >= 9
			love.resize = (w, h) -> DispatchEvent Event(Event.RESIZE), w, h

		-- call some more setup from here
		setup @

	-- Override this, called after Engine has been added to the stage.
	init: =>

	-- Override this, called when game gains focus
	focusGained: =>

	-- Override this, called when game loses focus
	focusLost: =>

	-- Updates the game, updating the Scene and Entities.
	update: =>
		LP.scene\updateLists!
		checkScene! if LP\sceneIsNull!
		LP.tweener\updateTweens! if LP.tweener.active and LP.tweener\hasTween!

		if LP.scene.active
			LP.scene\updateTweens! if LP.scene\hasTween!
			LP.scene\update!

		LP.scene\updateLists false

	-- Renders the game, rendering the Scene and Entities.
	render: =>
		if LP.screen.needsResize!
			LP.resize LP.windowWidth, LP.windowHeight
			LP.screen.needsResize = => false

		-- timing stuff
		t = love.timer.getDelta!

		@__frameLast = t if @__frameLast == 0

		-- render loop
		if LP.renderMode == RenderMode.Buffer
			LP.screen\swap!
			LP.screen\refresh!

		Draw.resetTarget!

		LP.scene\render! if LP.scene.visible

		LP.screen\redraw! if LP.renderMode == RenderMode.Buffer

		-- more timionStageng stuff
		t = love.timer.getTime!
		@__frameList[#@__frameList] = t - @__frameLast
		@__frameListSum += @__frameList[#@__frameList]
		if #@__frameList > 10
			@__frameListSum -= @__frameList[#@__frameList]
			@__frameList = [frame for frame in *@__frameList[2,]]

		LP.frameRate = 1000 / (@__frameListSum / #@__frameList)
		@__frameLast = t

	-- Sets the game's graphics properties. Override this to set them differently.
	setGraphicsProperties: =>
		if love._version_major >= 0 and love._version_minor >= 9
			-- love.window is Löve >= 0.9
			LP.windowWidth = love.window.getWidth!
			LP.windowHeight = love.window.getHeight!
		else
			LP.windowWidth = love.graphics.getWidth!
			LP.windowHeionStageght = love.graphics.getHeight!

		@resize! -- call resize once to initialize the screen

		-- set resize event
		AddEventListener Event.RESIZE, (e) -> @resize!

		AddEventListener Event.ACTIVATE, (e) ->
			LP.focused = true
			@focusGained!
			LP.scene\focusGained!

		AddEventListener Event.DEACTIVATE, (e) ->
			LP.focused = false
			@focusLost!
			LP.scene\focusLost!

	-- @private Event handler for window resize
	resize: =>
		LP.width = love.graphics.getWidth! if LP.width == 0
		LP.height = love.graphics.getHeight! if LP.height == 0

		-- calculate scale from width/height values
		if love._version_major >= 0 and love._version_minor >= 9
			-- love.window is Löve >= 0.9
			LP.windowWidth = love.window.getWidth!
			LP.windowHeight = love.window.getHeight!
		else
			LP.windowWidth = love.graphics.getWidth!
			LP.windowHeight = love.graphics.getHeight!

		LP.screen\scaleX love.graphics.getWidth! / LP.width
		LP.screen\scaleY love.graphics.getHeight! / LP.height
		LP.resize love.graphics.getWidth!, love.graphics.getHeight!

	-- @private Finish the setup
	-- HaxePunk this is onStage, but there isn't any async in the setup with Löve
	setup = =>
		@setGraphicsProperties!

		-- enable input
		Input.enable!

		-- switch scenes
		@checkScene! if not LP\sceneIsNull!

		-- game start
		@init!

	-- @private Framerate independent game loop.
	onEnterFrame = (e) =>

	-- @private Switch scenes if they've changed.
	checkScene = =>
		return if not LP\sceneIsNull!

		if LP.scene != nil
			LP.scene\endScene!
			LP.scene\updateLists!
			LP.scene\clearTweens! if LP.scene.autoClear and LP.scene\hasTween!
			-- TODO
			-- removeChild(LP.scene.sprite) if LP.scene.sprite
			LP\swapScene!
			-- TODO
			-- addChild LP.scene.sprite
			LP.camera = LP.scene.camera
			LP.scene\begin!
			LP.scene\updateLists!


Add the coveralls build url to the log

Add the coveralls build url to the log

coverage = require "coveralls.coverage"
require "json"

export ^

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
-- This copyright applies to formencodepart and formencode
formencodepart = (s) ->
	return s and s\gsub "%W", (c) ->
			if c ~= " "
				string.format "%%%02x", c\byte!
			else
				"+"

formencode = (form) ->
	result = {}
	if form[1] -- Array of ordered { name, value }
		for _, field in ipairs form  do
			table.insert result, formencodepart(field.name).."="..formencodepart(field.value)
	else -- Unordered map of name -> value
		for name, value in pairs(form) do
			table.insert result, formencodepart(name).."="..formencodepart(value)

	table.concat result, "&"

class coveralls extends coverage.CodeCoverage
	Travis: "travis-ci"
	Jenkins: "jenkins"
	Semaphore: "semaphore"
	Circle: "circleci"
	Local: "local"

	new: =>
		@source_files = {}
		-- The continuous integration service
		@service_name = @@Travis
		-- The continuous integration service job id
		@service_job_id = nil
		-- The secret repo token for your repository, found at the bottom of
		-- your repository's page on Coveralls.
		@repo_token = nil

		@dirname = 'src'
		@ext = '*.lua'

		super!

	coverDir: (dirname, ext = '*.lua') =>
		dir = require "pl.dir"
		@cover f for f in *(dir.getfiles dirname, ext)
		@coverDir d, ext for d in *(dir.getdirectories dirname)

	cover: (fname) =>
		file_coverage = @file_coverage fname
		return unless file_coverage
		c = file_coverage.coverage
		length = #c
		for k, v in pairs c
			length = k if k > length

		for i = 1, length
			file_coverage.coverage[i] = json.util.null if c[i] == nil

		table.insert @source_files, file_coverage

	send: =>
		if not @service_job_id
			env = nil
			switch @service_name
				when @@Travis
					env = 'TRAVIS_JOB_ID'
				when @@Jenkins
					env = 'BUILD_NUMBER'
				when @@Semaphore
					env = 'SEMAPHORE_BUILD_NUMBER'
				when @@Circle
					env = 'CIRCLE_BUILD_NUM'
				when @@Local
					return

			@service_job_id = os.getenv env

		assert @service_job_id, "A service_job_id must be specified"

		form_data = formencode {
			json: json.encode {
				service_name: @service_name,
				service_job_id: @service_job_id,
				repo_token: @repo_token,
				source_files: @source_files,
			}
		}

		https = require "ssl.https"
		body, code, headers, status = https.request 'https://coveralls.io/api/v1/jobs', form_data

		msg = json.decode body

		assert code == 200, string.format "Error updating Coveralls: (status: %s) (response: %s)", status, msg.message
		print msg.url

Coveralls = coveralls!

coverage = require "coveralls.coverage"
require "json"

export ^

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
-- This copyright applies to formencodepart and formencode
formencodepart = (s) ->
	return s and s\gsub "%W", (c) ->
			if c ~= " "
				string.format "%%%02x", c\byte!
			else
				"+"

formencode = (form) ->
	result = {}
	if form[1] -- Array of ordered { name, value }
		for _, field in ipairs form  do
			table.insert result, formencodepart(field.name).."="..formencodepart(field.value)
	else -- Unordered map of name -> value
		for name, value in pairs(form) do
			table.insert result, formencodepart(name).."="..formencodepart(value)

	table.concat result, "&"

class coveralls extends coverage.CodeCoverage
	Travis: "travis-ci"
	Jenkins: "jenkins"
	Semaphore: "semaphore"
	Circle: "circleci"
	Local: "local"

	new: =>
		@source_files = {}
		-- The continuous integration service
		@service_name = @@Travis
		-- The continuous integration service job id
		@service_job_id = nil
		-- The secret repo token for your repository, found at the bottom of
		-- your repository's page on Coveralls.
		@repo_token = nil

		@dirname = 'src'
		@ext = '*.lua'

		super!

	coverDir: (dirname, ext = '*.lua') =>
		dir = require "pl.dir"
		@cover f for f in *(dir.getfiles dirname, ext)
		@coverDir d, ext for d in *(dir.getdirectories dirname)

	cover: (fname) =>
		file_coverage = @file_coverage fname
		return unless file_coverage
		c = file_coverage.coverage
		length = #c
		for k, v in pairs c
			length = k if k > length

		for i = 1, length
			file_coverage.coverage[i] = json.util.null if c[i] == nil

		table.insert @source_files, file_coverage

	send: =>
		if not @service_job_id
			env = nil
			switch @service_name
				when @@Travis
					env = 'TRAVIS_JOB_ID'
				when @@Jenkins
					env = 'BUILD_NUMBER'
				when @@Semaphore
					env = 'SEMAPHORE_BUILD_NUMBER'
				when @@Circle
					env = 'CIRCLE_BUILD_NUM'
				when @@Local
					return

			@service_job_id = os.getenv env

		assert @service_job_id, "A service_job_id must be specified"

		form_data = formencode {
			json: json.encode {
				service_name: @service_name,
				service_job_id: @service_job_id,
				repo_token: @repo_token,
				source_files: @source_files,
			}
		}

		https = require "ssl.https"
		body, code, headers, status = https.request 'https://coveralls.io/api/v1/jobs', form_data

		msg = json.decode body

		assert code == 200, string.format "Error updating Coveralls: (status: %s) (response: %s)", status, msg.message

Coveralls = coveralls!


Added tweens.misc.VarTween

Added tweens.misc.VarTween

require "LunoPunk.Tween"

export ^

-- Tweens a numeric public property of an Object.
class VarTween extends Tween
	-- Constructor.
	-- @param	complete	Optional completion callback.
	-- @param	type		Tween type.
	new: (complete, type) =>
		-- Tween information.
		@__object = nil
		@__property = ""
		@__start, @__range = 0, 0

		super 0, type, complete

	-- Tweens a numeric public property.
	-- @param	object		The object containing the property.
	-- @param	property	The name of the property (eg. "x").
	-- @param	to			Value to tween to.
	-- @param	duration	Duration of the tween.
	-- @param	ease		Optional easer function.
	tween: (object, property, to, duration, ease = null) =>
		@__object = object
		@__ease = ease

		-- Check to make sure we have valid parameters
		assert type(object) == "table", "A valid table was not passed."

		@__property = property
		a = tonumber @__object[property]

		-- Check if the variable is a number
		assert a, string.format 'The property "%s" is not numeric.', property

		@__start = a
		@__range = to - @__start
		@__target = duration
		@start!

	-- @private Updates the Tween.
	update: =>
		super!
		@__object[@__property] = @__start + @__range * @t


removed old bootstrap code

removed old bootstrap code

--lapis = require "lapis"
lazuli = require "lazuli"

class extends lazuli.Application
  "/": =>
    "Welcome to Lazuli!"


trigger: fix pattern, add list, fix deletion, add show

Former-commit-id: d8b91e18058b7128dd6808ca1380357d61673a51 [formerly b1b9a66c1f3203965abd1331d2517e8818ef0ec7]
Former-commit-id: abb3798e6aed2914f8087c61b4b8ab2264008f9e

sql = require'lsqlite3'

openDb = ->
  db = sql.open 'cache/trigger.sql'

  db\exec [[
    CREATE TABLE IF NOT EXISTS trigger (
      name UNIQUE ON CONFLICT REPLACE,
      pattern UNIQUE ON CONFLICT REPLACE,
      funcstr
    );
    ]]
  return db

triggerHelp  = (source, destination, argument) =>
   @Msg 'privmsg', destination, source, 'Usage: !trigger add <name>|<lua pattern>|<lua code>. Ex: !trigger add help|^!help (%w+)|say "%s, you need help. Delete: !trigger del <name>"'

-- Construct a safe environ for trigger with two functions; say and reply
--- Preferrably you should be able to cross-call modules here to make aliasing possible
sandbox = (func) ->
  (source, destination, ...) =>
    -- Store args so we can use them in env
    arg = {...}
    env =
      say: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      reply: (str) ->
        @Msg 'privmsg', destination, source, source.nick..': '..str, unpack(arg),
      print: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      simplehttp:require'simplehttp'
      json:require'json'
      :string
      :type
      :tostring
      :tonumber
      :ipars
      :pairs
      :table
      :next

    --setfenv func, setmetatable(env, {__index: _G })
    setfenv func, env
    success, err = pcall func, arg
    if err
      @Msg 'privmsg', destination, source, err
    else
      success

-- Construct a safe function to run and return a handler
triggerHandler = (source, destination, funcstr) =>
  func, err = loadstring funcstr
  unless func
    @Msg 'privmsg', destination, source, 'Trigger error: %s', err
  else
    sandbox(func)

-- Register the command
regCommand = (source, destination, name, pattern, funcstr) =>
  db = openDb!
  ins = db\prepare "INSERT INTO trigger (name, pattern, funcstr) VALUES(?, ?, ?)"
  code = ins\bind_values name, pattern, funcstr
  code = ins\step!
  code = ins\finalize!
  db\close!

  @RegisterCommand 'trigger', pattern, triggerHandler(@, source, destination, funcstr)

delCommand = (source, destination, name) =>
  db = openDb!
  -- Find pattern which is used for deletion of handler
  stmt = db\prepare "SELECT pattern FROM trigger WHERE name = ?"
  code = stmt\bind_values name
  code = stmt\step!
  if code == sqlite3.DONE
    -- Invalid name
    db\close!
    @Msg 'privmsg', destination, source, 'Invalid name'
    return
  pattern = stmt\get_values()[1]

  -- Delete trigger from db
  ins = db\prepare "DELETE FROM trigger WHERE name = ?"
  code = ins\bind_values name
  code = ins\step!
  code = ins\finalize!
  db\close!

  @UnregisterCommand 'triggers', pattern
  @Msg 'privmsg', destination, source, "Trigger #{pattern} deleted"

listTriggers = (source, destination) =>
  db = openDb!
  stmt = db\prepare "SELECT name FROM trigger"
  out = {}
  for row in stmt\nrows! do
    table.insert out, row.name

  db\close!

  if #out > 0
    @Msg 'privmsg', destination, source, "Triggers: "..table.concat(out, ', ')

showTrigger = (source, destination, name) =>
  db = openDb!
  stmt = db\prepare "SELECT * FROM trigger WHERE name = ?"
  code = stmt\bind_values name
  for row in stmt\nrows! do
    @Msg 'privmsg', destination, source, "Trigger: \002#{row.name}\002, pattern: #{row.pattern}, code: #{row.funcstr}"
  db\close!

-- Register commands on startup
db = openDb!
for row in db\nrows 'SELECT pattern, funcstr FROM trigger'
  ivar2\RegisterCommand 'triggers', row.pattern, triggerHandler(ivar2, nil, nil, row.funcstr)
db\close!

PRIVMSG:
  '^%ptrigger$': triggerHelp
  '^%ptrigger add (.+)|(.+)|(.+)$': regCommand
  '^%ptrigger del (.+)$': delCommand
  '^%ptrigger list$': listTriggers
  '^%ptrigger show (.+)$': showTrigger

sql = require'lsqlite3'

openDb = ->
  db = sql.open 'cache/trigger.sql'

  db\exec [[
    CREATE TABLE IF NOT EXISTS trigger (
      name UNIQUE ON CONFLICT REPLACE,
      pattern UNIQUE ON CONFLICT REPLACE,
      funcstr
    );
    ]]
  return db

triggerHelp  = (source, destination, argument) =>
   @Msg 'privmsg', destination, source, 'Usage: !trigger add <name>|<lua pattern>|<lua code>. Ex: !trigger add help|^!help (%w+)|say "%s, you need help"'

-- Construct a safe environ for trigger with two functions; say and reply
--- Preferrably you should be able to cross-call modules here to make aliasing possible
sandbox = (func) ->
  (source, destination, ...) =>
    -- Store args so we can use them in env
    arg = {...}
    env =
      say: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      reply: (str) ->
        @Msg 'privmsg', destination, source, source.nick..': '..str, unpack(arg),
      print: (str) ->
        @Msg 'privmsg', destination, source, str, unpack(arg),
      simplehttp:require'simplehttp'
      json:require'json'
      :string
      :type
      :tostring
      :tonumber
      :ipars
      :pairs
      :table
      :next

    --setfenv func, setmetatable(env, {__index: _G })
    setfenv func, env
    success, err = pcall func, arg
    if err
      @Msg 'privmsg', destination, source, err
    else
      success

-- Construct a safe function to run and return a handler
triggerHandler = (source, destination, funcstr) =>
  func, err = loadstring funcstr
  unless func
    @Msg 'privmsg', destination, source, 'Trigger error: %s', err
  else
    sandbox(func)

-- Register the command
regCommand = (source, destination, name, pattern, funcstr) =>
  db = openDb!
  ins = db\prepare "INSERT INTO trigger (name, pattern, funcstr) VALUES(?, ?, ?)"
  code = ins\bind_values name, pattern, funcstr
  code = ins\step!
  code = ins\finalize!
  db\close!

  @RegisterCommand 'trigger', pattern, triggerHandler(@, source, destination, funcstr)

delCommand = (source, destination, name) =>
  db = openDb!
  -- Find pattern which is used for deletion of handler
  stmt = db\prepare "SELECT pattern FROM trigger WHERE name = ?"
  code = stmt\bind_values name
  code = stmt\step!
  if code == sqlite3.DONE
    -- Invalid name
    db\close!
    return
  pattern = stmt\get_values()[1]

  -- Delete trigger from db
  ins = db\prepare "DELETE FROM trigger WHERE name = ?"
  code = ins\bind_values pattern
  code = ins\step!
  code = ins\finalize!
  db\close!

  @UnregisterCommand 'triggers', pattern

-- Register commands on startup
db = openDb!
for row in db\nrows 'SELECT pattern, funcstr FROM trigger'
  ivar2\RegisterCommand 'triggers', row.pattern, triggerHandler(ivar2, nil, nil, row.funcstr)
db\close!

PRIVMSG:
  '%ptrigger$': triggerHelp
  '%ptrigger add (.+)|(.+)|(.+)$': regCommand
  '%ptrigger del (.+)$': delCommand