Spam users in console about directx level


Increased WriteUInt network id number

Increased WriteUInt network id number

--
-- 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 NetworkChangeState
	new: (key = '', keyValid = '', newValue, obj, len = 24, ply = NULL) =>
		@key = key
		@keyValid = keyValid
		@oldValue = obj[key]
		@newValue = newValue
		@ply = ply
		@time = CurTime()
		@rtime = RealTime()
		@stime = SysTime()
		@obj = obj
		@objID = obj.netID
		@len = len
		@rlen = len - 24 -- ID - 16 bits, variable id - 8 bits
		@networkChange = true
	GetPlayer: => @ply
	ChangedByClient: => not @networkChange or IsValid(@ply)
	ChangedByPlayer: => not @networkChange or IsValid(@ply)
	ChangedByServer: => not @networkChange or not IsValid(@ply)
	GetKey: => @keyValid
	GetVariable: => @keyValid
	GetVar: => @keyValid
	GetKeyInternal: => @key
	GetVariableInternal: => @key
	GetVarInternal: => @key
	GetNewValue: => @newValue
	GetValue: => @newValue
	NewValue: => @newValue
	GetOldValue: => @oldValue
	OldValue: => @oldValue
	CurTime: => @time
	GetCurTime: => @time
	GetReceiveTime: => @time
	GetReceiveStamp: => @time
	RealTime: => @rtime
	GetRealTime: => @rtime
	SysTime: => @stime
	GetSysTime: => @stime
	GetObject: => @obj
	GetNWObject: => @obj
	GetNetworkedObject: => @obj
	GetLength: => @rlen
	GetRealLength: => @len
	ChangedByNetwork: => @networkChange

	Revert: => @obj[@key] = @oldValue
	Apply: => @obj[@key] = @newValue

class NetworkedObject
	@Setup = =>
		@NW_Vars = {} if @NW_Vars == nil
		@NW_VarsTable = {}
		@NW_Objects = {}
		@NW_Waiting = {}
		@NW_WaitID = -1
		@NW_Setup = true
		@NW_NextVarID = -1 if @NW_NextVarID == nil
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_Remove = "PPM2.NW.Removed.#{@__name}"
		@NW_Rejected = "PPM2.NW.Rejected.#{@__name}"
		@NW_ReceiveID = "PPM2.NW.ReceiveID.#{@__name}"
		@NW_CooldownTimerCount = "PPM2_NW_CooldownTimerCount_#{@__name}"
		@NW_CooldownTimer = "PPM2_NW_CooldownTimer_#{@__name}"
		@NW_CooldownMessage = "PPM2_NW_CooldownMessage_#{@__name}"
		@NW_NextObjectID = 0
		@NW_NextObjectID_CL = 2 ^ 28

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)
			util.AddNetworkString(@NW_Remove)
			util.AddNetworkString(@NW_ReceiveID)
			util.AddNetworkString(@NW_Rejected)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) -> @OnNetworkedModify(ply, len)
		net.Receive @NW_Remove, (len = 0, ply = NULL) -> @OnNetworkedDelete(ply, len)
		net.Receive @NW_ReceiveID, (len = 0, ply = NULL) ->
			return if SERVER
			waitID = net.ReadUInt(16)
			netID = net.ReadUInt(16)
			obj = @NW_Waiting[waitID]
			@NW_Waiting[waitID] = nil
			return unless obj
			obj.NETWORKED = true
			@NW_Objects[obj.netID] = nil
			obj.netID = netID
			obj.waitID = nil
			@NW_Objects[netID] = obj
		net.Receive @NW_Rejected, (len = 0, ply = NULL) ->
			return if SERVER
			netID = net.ReadUInt(16)
			obj = @NW_Objects[netID]
			return unless obj
			return if obj.__LastReject and obj.__LastReject > RealTime()
			obj.__LastReject = RealTime() + 3
			obj.NETWORKED = false
			obj\Create()
	-- @__inherited = (child) => child.Setup(child)

	@AddNetworkVar = (getName = 'Var', readFunc = (->), writeFunc = (->), defValue, onSet = ((val) => val)) =>
		strName = "_NW_#{getName}"
		@NW_NextVarID += 1
		id = @NW_NextVarID
		tab = {:strName, :readFunc, :getName, :writeFunc, :defValue, :id, :onSet}
		table.insert(@NW_Vars, tab)
		@NW_VarsTable[id] = tab
		@__base[strName] = defValue
		@__base["Get#{getName}"] = => @[strName]
		@__base["Set#{getName}"] = (val = defValue, networkNow = true) =>
			oldVal = @[strName]
			@[strName] = val
			nevVal = onSet(@, val)
			state = NetworkChangeState(strName, getName, nevVal, @)
			state.networkChange = false
			@SetLocalChange(state)
			if networkNow and @NETWORKED and (CLIENT and @@NW_ClientsideCreation and @GetOwner() == LocalPlayer() or SERVER)
				net.Start(@@NW_Modify)
				net.WriteUInt(@GetNetworkID(), 16)
				net.WriteUInt(id, 16)
				writeFunc(nevVal)
				if CLIENT
					net.SendToServer()
				else
					net.Broadcast()
	@NetworkVar = (...) => @AddNetworkVar(...)

	@NW_ClientsideCreation = false
	@NW_RemoveOnPlayerLeave = true
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		return if SERVER and not @NW_ClientsideCreation
		if CLIENT
			netID = net.ReadUInt(16)
			creator = NULL
			creator = net.ReadEntity() if net.ReadBool()
			creator = NULL if not IsValid(creator)
			obj = @NW_Objects[netID] or @(netID)
			obj.NW_Player = creator
			obj.NETWORKED = true
			obj.CREATED_BY_SERVER = true
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			@OnNetworkedCreatedCallback(obj, ply, len)
		else
			ply[@NW_CooldownTimer] = ply[@NW_CooldownTimer] or 0
			ply[@NW_CooldownTimerCount] = ply[@NW_CooldownTimerCount] or 0

			if ply[@NW_CooldownTimer] < RealTime()
				ply[@NW_CooldownTimerCount] = 1
				ply[@NW_CooldownTimer] = RealTime() + 10
			else
				ply[@NW_CooldownTimerCount] += 1

			if ply[@NW_CooldownTimerCount] >= 3
				ply[@NW_CooldownMessage] = ply[@NW_CooldownMessage] or 0
				if ply[@NW_CooldownMessage] < RealTime()
					PPM2.Message 'Player ', ply, " is creating #{@__name} too quickly!"
					ply[@NW_CooldownMessage] = RealTime() + 1
				return

			waitID = net.ReadUInt(16)
			obj = @()
			obj.NW_Player = ply
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			obj\Create()
			timer.Simple 0.5, ->
				return if not IsValid(ply)
				net.Start(@NW_ReceiveID)
				net.WriteUInt(waitID, 16)
				net.WriteUInt(obj.netID, 16)
				net.Send(ply)
			@OnNetworkedCreatedCallback(obj, ply, len)
	@OnNetworkedCreatedCallback = (obj, ply = NULL, len = 0) => -- Override

	@OnNetworkedModify = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		unless obj
			if SERVER
				net.Start(@NW_Rejected)
				net.WriteUInt(id, 16)
				net.Send(ply)
			return
		return if IsValid(ply) and obj.NW_Player ~= ply
		varID = net.ReadUInt(16)
		varData = @NW_VarsTable[varID]
		return unless varData
		{:strName, :getName, :readFunc, :writeFunc, :onSet} = varData
		newVal = onSet(obj, readFunc())
		return if newVal == obj["Get#{getName}"](obj)
		state = NetworkChangeState(strName, getName, newVal, obj, len, ply)
		state\Apply()
		obj\NetworkDataChanges(state)
		if SERVER
			net.Start(@NW_Modify)
			net.WriteUInt(id, 16)
			net.WriteUInt(varID, 16)
			writeFunc(newVal)
			net.SendOmit(ply)
		@OnNetworkedModifyCallback(state)
	@OnNetworkedModifyCallback = (state) => -- Override

	@OnNetworkedDelete = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		obj\Remove(true)
		@OnNetworkedDeleteCallback(obj, ply, len)
	@OnNetworkedDeleteCallback = (obj, ply = NULL, len = 0) => -- Override

	@ReadNetworkData = =>
		output = {strName, {getName, readFunc()} for {:getName, :strName, :readFunc} in *@NW_Vars}
		return output

	new: (netID, localObject = false) =>
		@valid = true
		@NETWORKED = false
		@NETWORKED_PREDICT = false

		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
		else
			netID = -1 if netID == nil
			@netID = netID

		@@NW_Objects[@netID] = @
		@NW_Player = NULL if SERVER
		@NW_Player = LocalPlayer() if CLIENT
		@isLocal = localObject
		@NW_Player = LocalPlayer() if localObject

	GetOwner: => @NW_Player
	IsValid: => @valid
	IsNetworked: => @NETWORKED
	IsGoingToNetwork: => @NETWORKED_PREDICT
	SetIsGoingToNetwork: (val = @NETWORKED) => @NETWORKED_PREDICT = val
	IsLocal: => @isLocal
	IsLocalObject: => @isLocal
	GetNetworkID: => @netID
	NetworkID: => @netID
	NetID: => @netID
	Remove: (byClient = false) =>
		@@NW_Objects[@netID] = nil
		@valid = false
		if CLIENT and @isLocal and @NETWORKED and @@NW_ClientsideCreation
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			net.SendToServer()
		elseif SERVER and @NETWORKED
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			if not IsValid(@NW_Player) or not byClient
				net.Broadcast()
			else
				net.SendOmit(@NW_Player)

	NetworkDataChanges: (state) => -- Override
	SetLocalChange: (state) => -- Override
	ReadNetworkData: (len = 24, ply = NULL, silent = false) =>
		data = @@ReadNetworkData()
		states = [NetworkChangeState(key, keyValid, newVal, @, len, ply) for key, {keyValid, newVal} in pairs data]
		for state in *states
			state\Apply()
			@NetworkDataChanges(state) unless silent

	WriteNetworkData: => writeFunc(@[strName]) for {:strName, :writeFunc} in *@@NW_Vars

	SendVar: (Var = '') =>
		return if @[Var] == nil

	__tostring: => "[NetworkedObject:#{@netID}|#{@ent}]"

	Create: =>
		return if @NETWORKED
		return if CLIENT and (not @@NW_ClientsideCreation or @CREATED_BY_SERVER)
		@NETWORKED = true if SERVER
		@NETWORKED_PREDICT = true
		if SERVER
			net.Start(@@NW_Create)
			net.WriteUInt(@netID, 16)
			net.WriteBool(IsValid(@NW_Player))
			net.WriteEntity(@NW_Player) if IsValid(@NW_Player)
			@WriteNetworkData()
			filter = RecipientFilter()
			filter\AddAllPlayers()
			filter\RemovePlayer(@NW_Player) if IsValid(@NW_Player)
			net.Send(filter)
		else
			@@NW_WaitID += 1
			@waitID = @@NW_WaitID
			net.Start(@@NW_Create)
			net.WriteUInt(@waitID, 16)
			@WriteNetworkData()
			net.SendToServer()
			@@NW_Waiting[@waitID] = @
	NetworkTo: (targets = {}) =>
		net.Start(@@NW_Create)
		net.WriteUInt(@netID, 16)
		net.WriteBool(IsValid(@NW_Player))
		net.WriteEntity(@NW_Player) if IsValid(@NW_Player)
		@WriteNetworkData()
		net.Send(targets)

PPM2.NetworkedObject = NetworkedObject

--
-- 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 NetworkChangeState
	new: (key = '', keyValid = '', newValue, obj, len = 24, ply = NULL) =>
		@key = key
		@keyValid = keyValid
		@oldValue = obj[key]
		@newValue = newValue
		@ply = ply
		@time = CurTime()
		@rtime = RealTime()
		@stime = SysTime()
		@obj = obj
		@objID = obj.netID
		@len = len
		@rlen = len - 24 -- ID - 16 bits, variable id - 8 bits
		@networkChange = true
	GetPlayer: => @ply
	ChangedByClient: => not @networkChange or IsValid(@ply)
	ChangedByPlayer: => not @networkChange or IsValid(@ply)
	ChangedByServer: => not @networkChange or not IsValid(@ply)
	GetKey: => @keyValid
	GetVariable: => @keyValid
	GetVar: => @keyValid
	GetKeyInternal: => @key
	GetVariableInternal: => @key
	GetVarInternal: => @key
	GetNewValue: => @newValue
	GetValue: => @newValue
	NewValue: => @newValue
	GetOldValue: => @oldValue
	OldValue: => @oldValue
	CurTime: => @time
	GetCurTime: => @time
	GetReceiveTime: => @time
	GetReceiveStamp: => @time
	RealTime: => @rtime
	GetRealTime: => @rtime
	SysTime: => @stime
	GetSysTime: => @stime
	GetObject: => @obj
	GetNWObject: => @obj
	GetNetworkedObject: => @obj
	GetLength: => @rlen
	GetRealLength: => @len
	ChangedByNetwork: => @networkChange

	Revert: => @obj[@key] = @oldValue
	Apply: => @obj[@key] = @newValue

class NetworkedObject
	@Setup = =>
		@NW_Vars = {} if @NW_Vars == nil
		@NW_VarsTable = {}
		@NW_Objects = {}
		@NW_Waiting = {}
		@NW_WaitID = -1
		@NW_Setup = true
		@NW_NextVarID = -1 if @NW_NextVarID == nil
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_Remove = "PPM2.NW.Removed.#{@__name}"
		@NW_Rejected = "PPM2.NW.Rejected.#{@__name}"
		@NW_ReceiveID = "PPM2.NW.ReceiveID.#{@__name}"
		@NW_CooldownTimerCount = "PPM2_NW_CooldownTimerCount_#{@__name}"
		@NW_CooldownTimer = "PPM2_NW_CooldownTimer_#{@__name}"
		@NW_CooldownMessage = "PPM2_NW_CooldownMessage_#{@__name}"
		@NW_NextObjectID = 0
		@NW_NextObjectID_CL = 2 ^ 28

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)
			util.AddNetworkString(@NW_Remove)
			util.AddNetworkString(@NW_ReceiveID)
			util.AddNetworkString(@NW_Rejected)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) -> @OnNetworkedModify(ply, len)
		net.Receive @NW_Remove, (len = 0, ply = NULL) -> @OnNetworkedDelete(ply, len)
		net.Receive @NW_ReceiveID, (len = 0, ply = NULL) ->
			return if SERVER
			waitID = net.ReadUInt(16)
			netID = net.ReadUInt(16)
			obj = @NW_Waiting[waitID]
			@NW_Waiting[waitID] = nil
			return unless obj
			obj.NETWORKED = true
			@NW_Objects[obj.netID] = nil
			obj.netID = netID
			obj.waitID = nil
			@NW_Objects[netID] = obj
		net.Receive @NW_Rejected, (len = 0, ply = NULL) ->
			return if SERVER
			netID = net.ReadUInt(16)
			obj = @NW_Objects[netID]
			return unless obj
			return if obj.__LastReject and obj.__LastReject > RealTime()
			obj.__LastReject = RealTime() + 3
			obj.NETWORKED = false
			obj\Create()
	-- @__inherited = (child) => child.Setup(child)

	@AddNetworkVar = (getName = 'Var', readFunc = (->), writeFunc = (->), defValue, onSet = ((val) => val)) =>
		strName = "_NW_#{getName}"
		@NW_NextVarID += 1
		id = @NW_NextVarID
		tab = {:strName, :readFunc, :getName, :writeFunc, :defValue, :id, :onSet}
		table.insert(@NW_Vars, tab)
		@NW_VarsTable[id] = tab
		@__base[strName] = defValue
		@__base["Get#{getName}"] = => @[strName]
		@__base["Set#{getName}"] = (val = defValue, networkNow = true) =>
			oldVal = @[strName]
			@[strName] = val
			nevVal = onSet(@, val)
			state = NetworkChangeState(strName, getName, nevVal, @)
			state.networkChange = false
			@SetLocalChange(state)
			if networkNow and @NETWORKED and (CLIENT and @@NW_ClientsideCreation and @GetOwner() == LocalPlayer() or SERVER)
				net.Start(@@NW_Modify)
				net.WriteUInt(@GetNetworkID(), 16)
				net.WriteUInt(id, 16)
				writeFunc(nevVal)
				if CLIENT
					net.SendToServer()
				else
					net.Broadcast()
	@NetworkVar = (...) => @AddNetworkVar(...)

	@NW_ClientsideCreation = false
	@NW_RemoveOnPlayerLeave = true
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		return if SERVER and not @NW_ClientsideCreation
		if CLIENT
			netID = net.ReadUInt(16)
			creator = NULL
			creator = net.ReadEntity() if net.ReadBool()
			creator = NULL if not IsValid(creator)
			obj = @NW_Objects[netID] or @(netID)
			obj.NW_Player = creator
			obj.NETWORKED = true
			obj.CREATED_BY_SERVER = true
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			@OnNetworkedCreatedCallback(obj, ply, len)
		else
			ply[@NW_CooldownTimer] = ply[@NW_CooldownTimer] or 0
			ply[@NW_CooldownTimerCount] = ply[@NW_CooldownTimerCount] or 0

			if ply[@NW_CooldownTimer] < RealTime()
				ply[@NW_CooldownTimerCount] = 1
				ply[@NW_CooldownTimer] = RealTime() + 10
			else
				ply[@NW_CooldownTimerCount] += 1

			if ply[@NW_CooldownTimerCount] >= 3
				ply[@NW_CooldownMessage] = ply[@NW_CooldownMessage] or 0
				if ply[@NW_CooldownMessage] < RealTime()
					PPM2.Message 'Player ', ply, " is creating #{@__name} too quickly!"
					ply[@NW_CooldownMessage] = RealTime() + 1
				return

			waitID = net.ReadUInt(16)
			obj = @()
			obj.NW_Player = ply
			obj.NETWORKED_PREDICT = true
			obj\ReadNetworkData()
			obj\Create()
			timer.Simple 0.5, ->
				return if not IsValid(ply)
				net.Start(@NW_ReceiveID)
				net.WriteUInt(waitID, 16)
				net.WriteUInt(obj.netID, 16)
				net.Send(ply)
			@OnNetworkedCreatedCallback(obj, ply, len)
	@OnNetworkedCreatedCallback = (obj, ply = NULL, len = 0) => -- Override

	@OnNetworkedModify = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		unless obj
			if SERVER
				net.Start(@NW_Rejected)
				net.WriteUInt(id, 16)
				net.Send(ply)
			return
		return if IsValid(ply) and obj.NW_Player ~= ply
		varID = net.ReadUInt(16)
		varData = @NW_VarsTable[varID]
		return unless varData
		{:strName, :getName, :readFunc, :writeFunc, :onSet} = varData
		newVal = onSet(obj, readFunc())
		return if newVal == obj["Get#{getName}"](obj)
		state = NetworkChangeState(strName, getName, newVal, obj, len, ply)
		state\Apply()
		obj\NetworkDataChanges(state)
		if SERVER
			net.Start(@NW_Modify)
			net.WriteUInt(id, 16)
			net.WriteUInt(varID, 8)
			writeFunc(newVal)
			net.SendOmit(ply)
		@OnNetworkedModifyCallback(state)
	@OnNetworkedModifyCallback = (state) => -- Override

	@OnNetworkedDelete = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		obj\Remove(true)
		@OnNetworkedDeleteCallback(obj, ply, len)
	@OnNetworkedDeleteCallback = (obj, ply = NULL, len = 0) => -- Override

	@ReadNetworkData = =>
		output = {strName, {getName, readFunc()} for {:getName, :strName, :readFunc} in *@NW_Vars}
		return output

	new: (netID, localObject = false) =>
		@valid = true
		@NETWORKED = false
		@NETWORKED_PREDICT = false

		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
		else
			netID = -1 if netID == nil
			@netID = netID

		@@NW_Objects[@netID] = @
		@NW_Player = NULL if SERVER
		@NW_Player = LocalPlayer() if CLIENT
		@isLocal = localObject
		@NW_Player = LocalPlayer() if localObject

	GetOwner: => @NW_Player
	IsValid: => @valid
	IsNetworked: => @NETWORKED
	IsGoingToNetwork: => @NETWORKED_PREDICT
	SetIsGoingToNetwork: (val = @NETWORKED) => @NETWORKED_PREDICT = val
	IsLocal: => @isLocal
	IsLocalObject: => @isLocal
	GetNetworkID: => @netID
	NetworkID: => @netID
	NetID: => @netID
	Remove: (byClient = false) =>
		@@NW_Objects[@netID] = nil
		@valid = false
		if CLIENT and @isLocal and @NETWORKED and @@NW_ClientsideCreation
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			net.SendToServer()
		elseif SERVER and @NETWORKED
			net.Start(@@NW_Remove)
			net.WriteUInt(@netID, 16)
			if not IsValid(@NW_Player) or not byClient
				net.Broadcast()
			else
				net.SendOmit(@NW_Player)

	NetworkDataChanges: (state) => -- Override
	SetLocalChange: (state) => -- Override
	ReadNetworkData: (len = 24, ply = NULL, silent = false) =>
		data = @@ReadNetworkData()
		states = [NetworkChangeState(key, keyValid, newVal, @, len, ply) for key, {keyValid, newVal} in pairs data]
		for state in *states
			state\Apply()
			@NetworkDataChanges(state) unless silent

	WriteNetworkData: => writeFunc(@[strName]) for {:strName, :writeFunc} in *@@NW_Vars

	SendVar: (Var = '') =>
		return if @[Var] == nil

	__tostring: => "[NetworkedObject:#{@netID}|#{@ent}]"

	Create: =>
		return if @NETWORKED
		return if CLIENT and (not @@NW_ClientsideCreation or @CREATED_BY_SERVER)
		@NETWORKED = true if SERVER
		@NETWORKED_PREDICT = true
		if SERVER
			net.Start(@@NW_Create)
			net.WriteUInt(@netID, 16)
			net.WriteBool(IsValid(@NW_Player))
			net.WriteEntity(@NW_Player) if IsValid(@NW_Player)
			@WriteNetworkData()
			filter = RecipientFilter()
			filter\AddAllPlayers()
			filter\RemovePlayer(@NW_Player) if IsValid(@NW_Player)
			net.Send(filter)
		else
			@@NW_WaitID += 1
			@waitID = @@NW_WaitID
			net.Start(@@NW_Create)
			net.WriteUInt(@waitID, 16)
			@WriteNetworkData()
			net.SendToServer()
			@@NW_Waiting[@waitID] = @
	NetworkTo: (targets = {}) =>
		net.Start(@@NW_Create)
		net.WriteUInt(@netID, 16)
		net.WriteBool(IsValid(@NW_Player))
		net.WriteEntity(@NW_Player) if IsValid(@NW_Player)
		@WriteNetworkData()
		net.Send(targets)

PPM2.NetworkedObject = NetworkedObject


textutils: new module with some helpful trix for filters

Former-commit-id: 461a96547cbcf31f1f6cc42f9be64db1cd0fe6bf [formerly 4960a2f67da21180d158e1f0468ff05babbf0be2]
Former-commit-id: b18440fb2a593891057c708f6b45d922cfc22c14

PRIVMSG:
  '^%pupper (.+)$': (source, destination, arg) =>
    say arg\upper!
  '^%plower (.+)$': (source, destination, arg) =>
    say arg\lower!
  '^%preverse (.+)$': (source, destination, arg) =>
    say arg\reverse!
  '^%plen (.+)$': (source, destination, arg) =>
    say tostring(arg\len!)
  '^%pnicks$': (source, destination) =>
    say table.concat([n for n,k in pairs(ivar2.channels[destination].nicks)], ' ')
  '^%prandom (.+)$': (source, destination, arg) =>
    words = [word for word in arg\gmatch('%S+')]
    say words[math.random(1, #words)]

error: pathspec 'modules/textutils.moon' did not match any file(s) known to git


illness: new module

illness: new module

-- thanks to byte[] for sicknesses :-)

illnesses = {
  [[nany vgpuvat]],
  [[nany cnva]],
  [[nfgvtzngvfz]],
  [[onq oerngu]],
  [[oryyl ohggba qvfpunetr]],
  [[oyheerq ivfvba pnhfrf]],
  [[oyheel ivfvba]],
  [[oyheel ivfvba]],
  [[objry vapbagvarapr]],
  [[pbyq fberf]],
  [[pbafgvcngvba]],
  [[peno yvpr]],
  [[qnaqehss]],
  [[qvfrnfrf bs gur rlr]],
  [[rwnphyngvba]],
  [[rerpgvba ceboyrzf]],
  [[rlr pbaqvgvbaf]],
  [[rlr qvfrnfrf]],
  [[rlr rkrepvfrf]],
  [[rlr synfurf]],
  [[rlr sybngref]],
  [[rlr urnygu]],
  [[rlr erqarff]],
  [[rlr fgenva flzcgbzf]],
  [[rlr ivfvba]],
  [[rlr ivgnzvaf]],
  [[srpny vapbagvarapr]],
  [[svffher]],
  [[synxl fxva]],
  [[sybngref va gur rlr]],
  [[travgny jnegf]],
  [[tynhpbzn flzcgbzf]],
  [[unve ybff]],
  [[urnq yvpr]],
  [[urzzbeubvqf]],
  [[uvpphcf]],
  [[ubj gb vzcebir rlrfvtug]],
  [[vagenbphyne cerffher]],
  [[veevgnoyr objry flaqebzr]],
  [[vgpuvat]],
  [[wbpx vgpu]],
  [[ynfre rlr fhetrel]],
  [[ynfre fhetrel sbe rlrf]],
  [[ynfvx]],
  [[ynfvx rlr fhetrel]],
  [[ynfvx fhetrel pbfg]],
  [[zna obbof]],
  [[zrzbel ceboyrzf]],
  [[zrabcnhfr]],
  [[zbhgu hypref]],
  [[anvy ovgvat]],
  [[beny frk]],
  [[birejrvtug]],
  [[cvyrf]],
  [[cbylcunfvp fyrrc]],
  [[cbea nqqvpgvba]],
  [[cerzrafgehny flaqebzr]],
  [[erq snpr]],
  [[erfgyrff yrtf]],
  [[ergvany qrgnpuzrag]],
  [[fpnyl fxva]],
  [[fpnef]],
  [[funxl unaqf]],
  [[fzryyl srrg]],
  [[fabevat]],
  [[fgnzzrevat]],
  [[fgergpu znexf]],
  [[fjrngvat]],
  [[gbranvy vasrpgvba]],
  [[gbathr ceboyrzf]],
  [[hevanel vapbagvarapr]],
  [[inevpbfr irvaf]],
  [[ireehpnf]],
  [[ivfvba gurencl]],
  [[jvaq]],
}

illness = ->
  ivar2.util.rot13(illnesses[math.random(1, #illnesses)])

PRIVMSG:
  '^%pillness (.+)$': (s, d, nick) =>
    say "#{nick}, afflicted by #{illness!}"
  '^%pillness$': (s, d) =>
    say illness!

error: pathspec 'modules/illness.moon' did not match any file(s) known to git


anagram: new module

anagram: new module

Former-commit-id: ab8f33e1e4b6181c73811dafd942195c57bb3c52 [formerly ae3ab7d960d6b11205c60e3726e2bab25a01b424]
Former-commit-id: 1c11f5c655f293c5423841bbc9d6e072f0dd9eaf

html2unicode = require'html'

PRIVMSG:
  '^%panagram (.+)$': (s, d, arg) =>
    ivar2.util.simplehttp "http://www.wordsmith.org/anagram/anagram.cgi?t=50&anagram=" .. ivar2.util.urlEncode(arg), (s) ->
      s = s\match "(Displaying%s.-)</?[Pp][ />]"
      unless s
        return say "Did not find result"
      s = s\gsub "<[Bb][Rr][ />]", ", "
      -- Strip tags
      s = s\gsub "%b<>", ""
      say html2unicode(s)\gsub(":%s*,", ":")\gsub(",%s*$", "")

error: pathspec 'modules/anagram.moon' did not match any file(s) known to git


German Language

German Language

-- Copyright (C) 2017-2018 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.

-- editor stuffs

gui.ppm2.editor.eyes.separate = 'Use separated settings for eyes'
gui.ppm2.editor.eyes.url = 'Eye URL texture'
gui.ppm2.editor.eyes.url_desc = 'When uring eye URL texture; options below have no effect'

gui.ppm2.editor.eyes.lightwarp_desc = 'Lightwarp has effect only on EyeRefract eyes'
gui.ppm2.editor.eyes.lightwarp = "Lightwarp"
gui.ppm2.editor.eyes.desc1 = "Lightwarp texture URL input\nIt must be 256x16!"
gui.ppm2.editor.eyes.desc2 = "Glossiness strength\nThis parameters adjucts strength of real time reflections on eye\nTo see changes, set ppm2_cl_reflections convar to 1\nOther players would see reflections only with ppm2_cl_reflections set to 1\n0 - is matted; 1 - is mirror"

for {tprefix, prefix} in *{{'def', ''}, {'left', 'Left '}, {'right', 'Right '}}
	gui.ppm2.editor.eyes[tprefix].lightwarp.shader = "#{prefix}Use EyeRefract shader"
	gui.ppm2.editor.eyes[tprefix].lightwarp.cornera = "#{prefix}Use Eye Cornera diffuse"
	gui.ppm2.editor.eyes[tprefix].lightwarp.glossiness = "#{prefix}Glossiness"

	gui.ppm2.editor.eyes[tprefix].type = "#{prefix}Eye type"
	gui.ppm2.editor.eyes[tprefix].lines = "#{prefix}Eye lines"
	gui.ppm2.editor.eyes[tprefix].derp = "#{prefix}Derp eye"
	gui.ppm2.editor.eyes[tprefix].derp_strength = "#{prefix}Derp eye strength"
	gui.ppm2.editor.eyes[tprefix].iris_size = "#{prefix}Eye size"

	gui.ppm2.editor.eyes[tprefix].points_inside = "#{prefix}Eye lines points inside"
	gui.ppm2.editor.eyes[tprefix].width = "#{prefix}Eye width"
	gui.ppm2.editor.eyes[tprefix].height = "#{prefix}Eye height"

	gui.ppm2.editor.eyes[tprefix].pupil.width = "#{prefix}Pupil width"
	gui.ppm2.editor.eyes[tprefix].pupil.height = "#{prefix}Pupil height"
	gui.ppm2.editor.eyes[tprefix].pupil.size = "#{prefix}Pupil size"

	gui.ppm2.editor.eyes[tprefix].pupil.shift_x = "#{prefix}Pupil Shift X"
	gui.ppm2.editor.eyes[tprefix].pupil.shift_y = "#{prefix}Pupil Shift Y"
	gui.ppm2.editor.eyes[tprefix].pupil.rotation = "#{prefix}Eye rotation"

	gui.ppm2.editor.eyes[tprefix].background = "#{prefix}Eye background"
	gui.ppm2.editor.eyes[tprefix].pupil_size = "#{prefix}Pupil"
	gui.ppm2.editor.eyes[tprefix].top_iris = "#{prefix}Top eye iris"
	gui.ppm2.editor.eyes[tprefix].bottom_iris = "#{prefix}Bottom eye iris"
	gui.ppm2.editor.eyes[tprefix].line1 = "#{prefix}Eye line 1"
	gui.ppm2.editor.eyes[tprefix].line2 = "#{prefix}Eye line 2"
	gui.ppm2.editor.eyes[tprefix].reflection = "#{prefix}Eye reflection effect"
	gui.ppm2.editor.eyes[tprefix].effect = "#{prefix}Eye effect"

gui.ppm2.editor.generic.title = 'PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file = '%q - PPM/2 Pony Editor'
gui.ppm2.editor.generic.title_file_unsaved = '%q* - PPM/2 Pony Editor (unsaved changes!)'

gui.ppm2.editor.generic.yes = 'Yas!'
gui.ppm2.editor.generic.no = 'Noh!'
gui.ppm2.editor.generic.ohno = 'Onoh!'
gui.ppm2.editor.generic.okay = 'Okai ;w;'
gui.ppm2.editor.generic.datavalue = '%s\nData value: %q'
gui.ppm2.editor.generic.url = '%s\n\nLink goes to: %s'
gui.ppm2.editor.generic.url_field = 'URL Field'
gui.ppm2.editor.generic.spoiler = 'Mysterious spoiler'

gui.ppm2.editor.generic.restart.needed = 'Editor restart required'
gui.ppm2.editor.generic.restart.text = 'You should restart editor for applying change.\nRestart now?\nUnsaved data will lost!'
gui.ppm2.editor.generic.fullbright = 'Fullbright'
gui.ppm2.editor.generic.wtf = 'For some reason, your player has no NetworkedPonyData - Nothing to edit!\nTry ppm2_reload in your console and try to open editor again'

gui.ppm2.editor.io.random.title = 'Randomize!'
gui.ppm2.editor.io.newfile.title = 'New File'
gui.ppm2.editor.io.newfile.confirm = 'Really want to create a new file?'
gui.ppm2.editor.io.newfile.toptext = 'Reset'
gui.ppm2.editor.io.delete.confirm = 'Do you really want to delete that file?\nIt will be gone forever!\n(a long time!)'
gui.ppm2.editor.io.delete.title = 'Really Delete?'

gui.ppm2.editor.io.filename = 'Filename'
gui.ppm2.editor.io.hint = 'Open file by double click'
gui.ppm2.editor.io.reload = 'Reload file list'
gui.ppm2.editor.io.failed = 'Failed to import.'

gui.ppm2.editor.io.warn.oldfile = '!!! It may or may not work. You will be squished.'
gui.ppm2.editor.io.warn.text = "Currently, you did not stated your changes.\nDo you really want to open another file?"
gui.ppm2.editor.io.warn.header = 'Unsaved changes!'
gui.ppm2.editor.io.save.button = 'Save'
gui.ppm2.editor.io.save.text = 'Enter file name without ppm2/ and .dat\nTip: to save as autoload, type "_current" (without ")'
gui.ppm2.editor.io.wear = 'Apply changes (wear)'

gui.ppm2.editor.seq.standing = 'Standing'
gui.ppm2.editor.seq.move = 'Moving'
gui.ppm2.editor.seq.walk = 'Walking'
gui.ppm2.editor.seq.sit = 'Sit'
gui.ppm2.editor.seq.swim = 'Swim'
gui.ppm2.editor.seq.run = 'Run'
gui.ppm2.editor.seq.duckwalk = 'Crouch walk'
gui.ppm2.editor.seq.duck = 'Crouch'
gui.ppm2.editor.seq.jump = 'Jump'

gui.ppm2.editor.misc.race = 'Race'
gui.ppm2.editor.misc.weight = 'Weight'
gui.ppm2.editor.misc.size = 'Pony Size'
gui.ppm2.editor.misc.hide_weapons = 'Should hide weapons'
gui.ppm2.editor.misc.chest = 'Male chest buff'
gui.ppm2.editor.misc.gender = 'Gender'
gui.ppm2.editor.misc.wings = 'Wings Type'
gui.ppm2.editor.misc.flexes = 'Flexes controls'
gui.ppm2.editor.misc.no_flexes2 = 'No flexes on new model'
gui.ppm2.editor.misc.no_flexes_desc = 'You can disable separately any flex state controller\nSo these flexes can be modified with third-party addons (like PAC3)'

gui.ppm2.editor.misc.hide_pac3 = 'Hide entitites when using PAC3 entity'
gui.ppm2.editor.misc.hide_mane = 'Hide mane when using PAC3 entity'
gui.ppm2.editor.misc.hide_tail = 'Hide tail when using PAC3 entity'
gui.ppm2.editor.misc.hide_socks = 'Hide socks when using PAC3 entity'

gui.ppm2.editor.tabs.main = 'General'
gui.ppm2.editor.tabs.files = 'Files'
gui.ppm2.editor.tabs.old_files = 'Old Files'
gui.ppm2.editor.tabs.cutiemark = 'Cutiemark'
gui.ppm2.editor.tabs.head = 'Head anatomy'
gui.ppm2.editor.tabs.eyes = 'Eyes'
gui.ppm2.editor.tabs.face = 'Face'
gui.ppm2.editor.tabs.mouth = 'Mouth'
gui.ppm2.editor.tabs.left_eye = 'Left Eye'
gui.ppm2.editor.tabs.right_eye = 'Right Eye'
gui.ppm2.editor.tabs.mane_horn = 'Mane and Horn'
gui.ppm2.editor.tabs.mane = 'Mane'
gui.ppm2.editor.tabs.details = 'Details'
gui.ppm2.editor.tabs.url_details = 'URL Details'
gui.ppm2.editor.tabs.url_separated_details = 'URL Separated Details'
gui.ppm2.editor.tabs.ears = 'Ears'
gui.ppm2.editor.tabs.horn = 'Ears'
gui.ppm2.editor.tabs.back = 'Back'
gui.ppm2.editor.tabs.wings = 'Wings'
gui.ppm2.editor.tabs.left = 'Left'
gui.ppm2.editor.tabs.right = 'Right'
gui.ppm2.editor.tabs.neck = 'Neck'
gui.ppm2.editor.tabs.body = 'Pony body'
gui.ppm2.editor.tabs.tattoos = 'Tattoos'
gui.ppm2.editor.tabs.tail = 'Tail'
gui.ppm2.editor.tabs.hooves = 'Hooves anatomy'
gui.ppm2.editor.tabs.bottom_hoof = 'Bottom hoof'
gui.ppm2.editor.tabs.legs = 'Legs'
gui.ppm2.editor.tabs.socks = 'Legs'
gui.ppm2.editor.tabs.newsocks = 'New Legs'

gui.ppm2.editor.cutiemark.display = 'Display cutiemark'
gui.ppm2.editor.cutiemark.type = 'Cutiemark type'
gui.ppm2.editor.cutiemark.size = 'Cutiemark size'
gui.ppm2.editor.cutiemark.color = 'Cutiemark color'
gui.ppm2.editor.cutiemark.input = 'Cutiemark URL image input field\nShould be PNG or JPEG (works same as\nPAC3 URL texture)'

gui.ppm2.editor.face.eyelashes = 'Eyelashes'
gui.ppm2.editor.face.eyelashes_color = 'Eyelashes Color'
gui.ppm2.editor.face.eyebrows_color = 'Eyebrows Color'
gui.ppm2.editor.face.new_muzzle = 'Use new muzzle for male model'

gui.ppm2.editor.face.nose = 'Nose Color'
gui.ppm2.editor.face.lips = 'Lips Color'
gui.ppm2.editor.face.eyelashes_separate_phong = 'Separate Eyelashes Phong'
gui.ppm2.editor.face.eyebrows_glow = 'Glowing eyebrows'
gui.ppm2.editor.face.eyebrows_glow_strength = 'Eyebrows glow strength'
gui.ppm2.editor.face.inherit.lips = 'Inherit Lips Color from body'
gui.ppm2.editor.face.inherit.nose = 'Inherit Nose Color from body'

gui.ppm2.editor.mouth.fangs = 'Fangs'
gui.ppm2.editor.mouth.alt_fangs = 'Alternative Fangs'
gui.ppm2.editor.mouth.claw = 'Claw teeth'

gui.ppm2.editor.mouth.teeth = 'Teeth color'
gui.ppm2.editor.mouth.mouth = 'Mouth color'
gui.ppm2.editor.mouth.tongue = 'Tongue color'

gui.ppm2.editor.mane.type = 'Mane Type'
gui.ppm2.editor.mane.phong = 'Separate mane phong settings from body'
gui.ppm2.editor.mane.phong_sep = 'Separate upper and lower mane colors'
gui.ppm2.editor.mane.up.phong = 'Upper Mane Phong Settings'
gui.ppm2.editor.mane.down.phong = 'Lower Mane Phong Settings'
gui.ppm2.editor.mane.newnotice = 'Next options have effect only on new model'

for i = 1, 2
	gui.ppm2.editor.mane['color' .. i] = "Mane color #{i}"
	gui.ppm2.editor.mane.up['color' .. i] = "Upper mane color #{i}"
	gui.ppm2.editor.mane.down['color' .. i] = "Lower mane color #{i}"

for i = 1, 6
	gui.ppm2.editor.mane['detail_color' .. i] = "Mane detail color #{i}"
	gui.ppm2.editor.mane.up['detail_color' .. i] = "Upper mane color #{i}"
	gui.ppm2.editor.mane.down['detail_color' .. i] = "Lower mane color #{i}"

	gui.ppm2.editor.url_mane['desc' .. i] = "Mane URL Detail #{i} input field"
	gui.ppm2.editor.url_mane['color' .. i] = "Mane URL detail color #{i}"

	gui.ppm2.editor.url_mane.sep.up['desc' .. i] = "Upper mane URL Detail #{i} input field"
	gui.ppm2.editor.url_mane.sep.up['color' .. i] = "Upper Mane URL detail color #{i}"

	gui.ppm2.editor.url_mane.sep.down['desc' .. i] = "Lower mane URL Detail #{i} input field"
	gui.ppm2.editor.url_mane.sep.down['color' .. i] = "Lower Mane URL detail color #{i}"

gui.ppm2.editor.ears.bat = 'Bat pony ears'
gui.ppm2.editor.ears.size = 'Ears size'

gui.ppm2.editor.horn.detail_color = 'Horn detail color'
gui.ppm2.editor.horn.glowing_detail = 'Glowing Horn Detail'
gui.ppm2.editor.horn.glow_strength = 'Horn Glow Strength'
gui.ppm2.editor.horn.separate_color = 'Separate horn color from body'
gui.ppm2.editor.horn.color = 'Horn color'
gui.ppm2.editor.horn.magic = 'Horn magic color'
gui.ppm2.editor.horn.separate_magic_color = 'Separate magic color from eye color'
gui.ppm2.editor.horn.separate = 'Separate horn color from body'
gui.ppm2.editor.horn.separate_phong = 'Separate horn phong settings from body'

for i = 1, 3
	gui.ppm2.editor.horn.detail['desc' .. i] = "Horn URL detail #{i}"
	gui.ppm2.editor.horn.detail['color' .. i] = "URL Detail color #{i}"

gui.ppm2.editor.wings.separate_color = 'Separate wings color from body'
gui.ppm2.editor.wings.color = 'Wings color'
gui.ppm2.editor.wings.separate = 'Separate wings color from body'
gui.ppm2.editor.wings.separate_phong = 'Separate wings phong settings from body'
gui.ppm2.editor.wings.bat_color = 'Bat Wings color'
gui.ppm2.editor.wings.bat_skin_color = 'Bat Wings skin color'

gui.ppm2.editor.wings.normal = 'Normal wings'
gui.ppm2.editor.wings.bat = 'Bat wings'
gui.ppm2.editor.wings.bat_skin = 'Bat wings skin'

gui.ppm2.editor.wings.left.size = 'Left wing size'
gui.ppm2.editor.wings.left.fwd = 'Left Wing Forward'
gui.ppm2.editor.wings.left.up = 'Left Wing Up'
gui.ppm2.editor.wings.left.inside = 'Left Wing Inside'

gui.ppm2.editor.wings.right.size = 'Right wing size'
gui.ppm2.editor.wings.right.fwd = 'Right Wing Forward'
gui.ppm2.editor.wings.right.up = 'Right Wing Up'
gui.ppm2.editor.wings.right.inside = 'Right Wing Inside'

for i = 1, 3
	gui.ppm2.editor.wings.details.def['detail' .. i] = "Wings URL detail #{i}"
	gui.ppm2.editor.wings.details.def['color' .. i] = "URL Detail color #{i}"
	gui.ppm2.editor.wings.details.bat['detail' .. i] = "Bat wing URL detail #{i}"
	gui.ppm2.editor.wings.details.bat['color' .. i] = "Bat wing URL Detail color #{i}"
	gui.ppm2.editor.wings.details.batskin['detail' .. i] = "Bat wing skin URL detail #{i}"
	gui.ppm2.editor.wings.details.batskin['color' .. i] = "Bat wing skin URL Detail color #{i}"

gui.ppm2.editor.neck.height = 'Neck height'

gui.ppm2.editor.body.suit = 'Bodysuit'
gui.ppm2.editor.body.color = 'Body color'
gui.ppm2.editor.body.spine_length = 'Spine length'
gui.ppm2.editor.body.url_desc = 'Body detail URL image input fields\nShould be PNG or JPEG (works same as\nPAC3 URL texture)'

for i = 1, PPM2.MAX_BODY_DETAILS
	gui.ppm2.editor.body.detail['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail['color' .. i] = "Detail color #{i}"
	gui.ppm2.editor.body.detail['glow' .. i] = "Detail #{i} is glowing"
	gui.ppm2.editor.body.detail['glow_strength' .. i] = "Detail #{i} glow strength"

	gui.ppm2.editor.body.detail.url['desc' .. i] = "Detail #{i}"
	gui.ppm2.editor.body.detail.url['color' .. i] = "Detail color #{i}"

gui.ppm2.editor.tattoo.edit_keyboard = 'Edit using keyboard'
gui.ppm2.editor.tattoo.type = 'Type'
gui.ppm2.editor.tattoo.over = 'Tattoo over body details'
gui.ppm2.editor.tattoo.glow = 'Tattoo is glowing'
gui.ppm2.editor.tattoo.glow_strength = 'Tattoo glow strength'
gui.ppm2.editor.tattoo.color = 'Color of Tattoo'

gui.ppm2.editor.tattoo.tweak.rotate = 'Rotation'
gui.ppm2.editor.tattoo.tweak.x = 'X Position'
gui.ppm2.editor.tattoo.tweak.y = 'Y Position'
gui.ppm2.editor.tattoo.tweak.width = 'Width Scale'
gui.ppm2.editor.tattoo.tweak.height = 'Height Scale'

for i = 1, PPM2.MAX_TATTOOS
	gui.ppm2.editor.tattoo['layer' .. i] = "Tattoo layer #{i}"

gui.ppm2.editor.tail.type = 'Tail type'
gui.ppm2.editor.tail.size = 'Tail size'
gui.ppm2.editor.tail.separate = 'Separate tail phong settings from body'

for i = 1, 2
	gui.ppm2.editor.tail['color' .. i] = 'Tail color ' .. i

for i = 1, 6
	gui.ppm2.editor.tail['detail' .. i] = "Tail detail color #{i}"
	gui.ppm2.editor.tail.url['detail' .. i] = "Tail URL detail #{i}"
	gui.ppm2.editor.tail.url['color' .. i] = "Tail URL detail #{i}"

gui.ppm2.editor.hoof.fluffers = 'Hoof Fluffers'

gui.ppm2.editor.legs.height = 'Legs height'
gui.ppm2.editor.legs.socks.simple = 'Socks (simple texture)'
gui.ppm2.editor.legs.socks.model = 'Socks (as model)'
gui.ppm2.editor.legs.socks.color = 'Socks model color'
gui.ppm2.editor.legs.socks.texture = 'Socks Texture'
gui.ppm2.editor.legs.socks.url_texture = 'Socks URL texture'

for i = 1, 6
	gui.ppm2.editor.legs.socks['color' .. i] = 'Socks detail color ' .. i

gui.ppm2.editor.legs.newsocks.model = 'Socks (as new model)'

for i = 1, 3
	gui.ppm2.editor.legs.newsocks['color' .. i] = 'New Socks color ' .. i

gui.ppm2.editor.legs.newsocks.url = 'New Socks URL texture'

-- shared editor stuffs

gui.ppm2.editor.tattoo.help = "To exit edit mode, press Escape or click anywhere with mouse
To move tatto use WASD
To Scale higher/lower use Up/Down arrows
To Scale wider/smaller use Right/Left arrows
To rotate left/right use Q/E"

for name, data in pairs PPM2.PonyDataRegistry
	gui.ppm2.editor.reset[data.getFunc\lower()] = 'Reset ' .. data.getFunc

gui.ppm2.editor.phong.info = 'More info about Phong on wiki'
gui.ppm2.editor.phong.exponent = 'Phong Exponent - how strong reflective property\nof pony skin is\nSet near zero to get robotic looking of your\npony skin'
gui.ppm2.editor.phong.exponent_text = 'Phong Exponent'
gui.ppm2.editor.phong.boost.title = 'Phong Boost - multiplies specular map reflections'
gui.ppm2.editor.phong.boost.boost = 'Phong Boost'
gui.ppm2.editor.phong.tint.title = 'Tint color - what colors does reflect specular map\nWhite - Reflects all colors\n(In white room - white specular map)'
gui.ppm2.editor.phong.tint.tint = 'Tint color - what colors does reflect specular map\nWhite - Reflects all colors\n(In white room - white specular map)'
gui.ppm2.editor.phong.frensel.front.title = 'Phong Front - Fresnel 0 degree reflection angle multiplier'
gui.ppm2.editor.phong.frensel.front.front = 'Phong Front'
gui.ppm2.editor.phong.frensel.middle.title = 'Phong Middle - Fresnel 45 degree reflection angle multiplier'
gui.ppm2.editor.phong.frensel.middle.front = 'Phong Middle'
gui.ppm2.editor.phong.frensel.sliding.title = 'Phong Sliding - Fresnel 45 degree reflection angle multiplier'
gui.ppm2.editor.phong.frensel.sliding.front = 'Phong Sliding'
gui.ppm2.editor.phong.lightwarp = 'Lightwarp'
gui.ppm2.editor.phong.url_lightwarp = 'Lightwarp texture URL input\nIt must be 256x16!'
gui.ppm2.editor.phong.bumpmap = 'Bumpmap URL input'

gui.ppm2.editor.info.discord = "Join DBot's Discord!"
gui.ppm2.editor.info.ponyscape = "PPM/2 is a Ponyscape project"
gui.ppm2.editor.info.creator = "PPM/2 were created and being developed by DBot"
gui.ppm2.editor.info.newmodels = "New models were created by Durpy"
gui.ppm2.editor.info.cppmmodels = "CPPM Models (including pony hands) belong to UnkN', 'http://steamcommunity.com/profiles/76561198084938735"
gui.ppm2.editor.info.oldmodels = "Old models belong to Scentus and others"
gui.ppm2.editor.info.bugs = "Found a bug? Report here!"
gui.ppm2.editor.info.sources = "You can find sources here"
gui.ppm2.editor.info.githubsources = "Or on GitHub mirror"
gui.ppm2.editor.info.thanks = "Special thanks to everypony who criticized,\nhelped and tested PPM/2!"

-- other stuff

info.ppm2.fly.pegasus = 'You need to be a Pegasus or an Alicorn to fly!'
info.ppm2.fly.cannot = 'You can not %s right now.'

gui.ppm2.emotes.sad = 'Sad'
gui.ppm2.emotes.wild = 'Wild'
gui.ppm2.emotes.grin = 'Grin'
gui.ppm2.emotes.angry = 'Angry'
gui.ppm2.emotes.tongue = ':P'
gui.ppm2.emotes.angrytongue = '>:P'
gui.ppm2.emotes.pff = 'Pffff!'
gui.ppm2.emotes.kitty = ':3'
gui.ppm2.emotes.owo = 'oWo'
gui.ppm2.emotes.ugh = 'Uuugh'
gui.ppm2.emotes.lips = 'Lips lick'
gui.ppm2.emotes.scrunch = 'Scrunch'
gui.ppm2.emotes.sorry = 'Sorry'
gui.ppm2.emotes.wink = 'Wink'
gui.ppm2.emotes.right_wink = 'Right Wink'
gui.ppm2.emotes.licking = 'Licking'
gui.ppm2.emotes.suggestive_lips = 'Siggestive Lips lick'
gui.ppm2.emotes.suggestive_no_tongue = 'Siggestive w/o tongue'
gui.ppm2.emotes.gulp = 'Gulp'
gui.ppm2.emotes.blah = 'Blah blah blah'
gui.ppm2.emotes.happi = 'Happi'
gui.ppm2.emotes.happi_grin = 'Happi grin'
gui.ppm2.emotes.duck = 'DUCK'
gui.ppm2.emotes.ducks = 'DUCK INSANITY'
gui.ppm2.emotes.quack = 'QUACKS'
gui.ppm2.emotes.suggestive = 'Soggestive w/ tongue'

message.ppm2.emotes.invalid = 'No such emotion with ID: %s'

error: pathspec 'moon/dlib/i18n/ppm2/de.moon' did not match any file(s) known to git


duckduckgo: new module

duckduckgo: new module

Former-commit-id: 04ae2c370794fd4deb006ab247d2d4df115984cc [formerly bdcf72ce406bdcc22ecacacd6566cf4ed43babd5]
Former-commit-id: fc515231efd7d6b3053b30fcf8fc017de99a7788

json = require 'json'
simplehttp = require 'simplehttp'

urlEncode = (str, space) ->
  space = space or '+'

  str = str\gsub '([^%w ])', (c) ->
      string.format  "%%%02X", string.byte(c)
  return str\gsub(' ', space)

PRIVMSG:
  '^%pddg (.+)$': (source, destination, term) =>
    term = urlEncode term
    simplehttp "http://api.duckduckgo.com/?q=#{term}&format=json", (d) ->
      data = json.decode d
      out = {}
      topic = data.RelatedTopics[1] if data.RelatedTopics and #data.RelatedTopics>0
      if data.Heading == ''
        return
      table.insert out, "\002#{data.Heading}\002:"
      table.insert out, data.AbstractText unless data.AbstractText == ''
      table.insert out, data.Image unless data.Image == ''
      table.insert out, data.AbstractURL unless data.AbstractURL == ''
      table.insert out, data.Definition unless data.Definition == ''
      table.insert out, data.DefinitionText unless data.DefinitionText == ''
      if #out < 3
        table.insert out, topic.FirstURL
        table.insert out, topic.Text

      @Msg 'privmsg', destination, source, table.concat(out, ' ')

error: pathspec 'modules/duckduckgo.moon' did not match any file(s) known to git


alias: new module

alias: new module

Former-commit-id: 47e4343c160c017d34e64b5ce972c841bd76a49e [formerly 8791aa6808a495fb1cf511959588d0860c73b2e4]
Former-commit-id: 528bbc43f1db9104764226dfee07be35036647d1

moduleName = 'alias'
key = 'alias:aliases'

listAliases = =>
  out = {}
  for name,command in pairs(ivar2.persist[key] or {})
    table.insert(out, name)
  say "Aliases: #{table.concat(out, ', ')}"

showAlias = (s, d, name) =>
  c = ivar2.persist[key][name]
  if c then
    say "Alias: #{ivar2.util.bold name} => #{ivar2.util.bold c}"
  else
    reply "Noperino. Failerino."

aliasHelp = =>
  help = 'Usage: ¤alias add <name> <command>. Ex: ¤alias add ahelp alias help. Delete: ¤alias del <name>. List: ¤alias list. Show: ¤alias show <name>. Use § to denote | for piping commands."'
  patt = @ChannelCommandPattern('^%p', moduleName, destination)
  if patt == '^%p'
    patt = '!'
  help = help\gsub '¤', patt
  reply help

aliasHandler = (source, destination, pattern) =>
  return (source, destination, arg) =>
    patt = @ChannelCommandPattern('^%p', moduleName, destination)
    if patt == '^%p'
      patt = '!'
    pattern = pattern\gsub('§', '|'..patt)
    ivar2\DispatchCommand 'PRIVMSG', patt..pattern, source, destination

-- Register the command
regCommand = (source, destination, name, pattern) =>
  store = ivar2.persist[key] or {}
  store[name] = pattern
  ivar2.persist[key] = store
  patt = @ChannelCommandPattern('^%p', moduleName, destination)
  @RegisterCommand moduleName, patt..name, aliasHandler(@, source, destination, pattern)
  if source and destination
    reply "Registered new alias #{ivar2.util.bold name} for #{ivar2.util.bold pattern}"

-- Register aliases *after* module load
ivar2\Timer 'regAlias', 1, ->
  for name,command in pairs(ivar2.persist[key] or {})
    regCommand(ivar2, nil, nil, name, command)

delCommand = (source, destination, name) =>
  store = ivar2.persist[key] or {}
  store[name] = nil
  ivar2.persist[key] = store
  reply 'Ok, I guess'

PRIVMSG:
  '^%palias$': aliasHelp
  '^%palias help$': aliasHelp
  '^%palias add (%S+) (.+)$': regCommand
  '^%palias del (.+)$': delCommand
  '^%palias list$': listAliases
  '^%palias show (.+)$': showAlias

error: pathspec 'modules/alias.moon' did not match any file(s) known to git


clue: new module

clue: new module

Former-commit-id: f7865f518baf55226e7a02d5ab8153d7e8a5fbae [formerly 32e3437ef620fa7c02a704f971ba11c9758b22a8]
Former-commit-id: 9a4c153901c4bf3edda857badb69f10a0ae35008

-- Clue module requires clue data files and tosql.py from emnh/clue2

dbi = require 'DBI'
require'logging.console'
log = logging.console()
conn = false

attrs = {'grammar','reference', 'country', 'context', 'text'}

connect = ->
  conn, err = DBI.Connect('PostgreSQL', ivar2.config.dbname, ivar2.config.dbuser, ivar2.config.dbpass, ivar2.config.dbhost, ivar2.config.dbport)
  unless conn
    log\error "Unable to connect to DB: #{err}"
    return

  conn\autocommit(true)

dbh = ->
  connect! unless conn

  -- Check if connection is alive
  alive = conn\ping!
  connect! unless alive

  success, err = DBI.Do(conn, 'SELECT NOW()')
  unless success
    log\error "SQL Connection :#{err}"
    connect!

  return conn

lookup = (source, destination, lookup) =>
  stmt = dbh!\prepare [[
    SELECT
      tablename
    from
      pg_catalog.pg_tables
    where
      tablename like 'cl%'
    ]]
  stmt\execute!
  tables = {}
  for row in stmt\rows(true) -- true for column names
    tables[#tables+1] = row.tablename

  out = {}
  for dict in *tables
    out[dict] = {}
    stmt = dbh!\prepare [[
      SELECT
        *
      FROM
        ]]..dict..[[
      WHERE
        word = ?
      ]]
    stmt\execute lookup
    for row in stmt\rows(true) -- true for column names
      table.insert(out[dict], row)

  i = 0
  for dict, rows in pairs out
    res = {}
    dfrom = dict\sub(3, 4)
    to = dict\sub(5, 6)
    first = true
    for row in *rows
      i += 1
      if first
        table.insert(res, "[#{ivar2.util.bold row.word}]")
        table.insert(res, "(#{ivar2.util.italic dfrom}")
        table.insert(res, "-> #{ivar2.util.italic to})")
        first = false
      for attr in *attrs
        if row[attr] != nil and row[attr] != ''
          if attr == 'grammar'
            table.insert(res, "(#{row[attr]}. )")
          else
            table.insert(res, row[attr])
    say table.concat(res, ' ')
  if i == 0
    reply 'Nope.'

PRIVMSG:
  '^%pclue (.+)$': lookup

error: pathspec 'modules/clue.moon' did not match any file(s) known to git


betayr: new module

betayr: new module

-- yr.no API consumer, using the new unofficial API
import urlEncode, simplehttp, json, red, lightblue, utf8 from require'util'

apiBase = 'http://www.yr.no'
-- default
default_lang = 'en'

windSpeedToName = (ws, lang) ->
  windtext = ''
  n = (lang\match '^n')
  unless ws
    return windtext
  if ws > 32
    windtext = 'orkan!'
    unless n
      windtext = 'hurricane!'
  elseif ws > 28.5
    windtext = 'sterk storm'
    unless n
      windtext = 'violent storm'
  elseif ws > 24.5
    windtext = 'full storm'
    unless n
      windtext = 'storm'
  elseif ws > 20.8
    windtext = 'liten storm'
    unless n
      windtext = 'severe gale'
  elseif ws > 17.2
    windtext = 'sterk kuling'
    unless n
      windtext = 'gale'
  elseif ws > 13.9
    windtext = 'stiv kuling'
    unless n
      windtext = 'near gale'
  elseif ws > 10.8
    windtext = 'liten kuling'
    unless n
      windtext = 'strong breeze'
  elseif ws > 8
    windtext = 'frisk bris'
    unless n
      windtext = 'fresh breeze'
  elseif ws > 5.5
    windtext = 'laber bris'
    unless n
      windtext = 'moderate breeze'
  elseif ws > 3.4
    windtext = 'lett bris'
    unless n
      windtext = 'gentle breeze'
  elseif ws > 1.5
    windtext = 'svak vind'
    unless n
      windtext = 'light breeze'
  elseif ws > 0.3
    windtext = 'flau vind'
    unless n
      windtext = 'light air'
  else
    windtext = 'vindstille'
    unless n
      windtext = 'calm'
  return windtext

arrow = (dir) ->
  -- Turn the direction of arrow to the way wind is coming from
  utf8arrow = utf8.arrow
  ndir = dir\gsub '.', (n) ->
    if     n == 'N' then n = 'S'
    elseif n == 'E' then n = 'W'
    elseif n == 'S' then n = 'N'
    elseif n == 'W' then n = 'E'
    return n
  utf8arrow(ndir)

color = (value) ->
  if tonumber(value) > 0 then
    return red
  else
    return lightblue

deg2name = (deg) ->
  val = math.floor((deg/22.5)+.5)
  arr = {"N","NNE","NE","ENE","E","ESE", "SE", "SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"}
  return arr[ (val % 16) +1 ]

getLang = (self, source, destination) ->
  lang = ivar2.persist['yr:lang:'..source.nick]
  switch lang
    when 'stad'
      return 'nn'
    when 'sted'
      return 'nb'
    when 'place'
      return 'en'
    -- else pass

  clocale = @DestinationLocale(destination)
  if (clocale\match('^nn'))
    return 'nn'
  elseif(clocale\match('^nb'))
    return 'nb'

  return default_lang

getLocation = (arg, lang) ->
  lang = lang or default_lang
  data = simplehttp apiBase..'/api/v0/locations/suggest?language='..lang..'&q='..urlEncode(arg)
  js = json.decode(data)
  if js.totalResults == 0
    return nil, 'No results'
  locations = js['_embedded']['location']
  _, location = next locations
  return location

getPlace = (self, source, destination, input) ->
  if not input or input == ''
    persist = self.persist['yr:place:'..source.nick]
    unless persist
      patt = @ChannelCommandPattern('^%pset yr <location>', "yr", destination)\sub(1)
      patt = patt\gsub('^%^%%p', '!')
      @Msg('privmsg', destination, source, 'Set your location first. Like this: '..patt)
      return
    input = persist

  return input

handleTempLookup = (source, destination, arg) =>
  place = getPlace @, source, destination, arg
  unless place
    return
  lang = getLang(@, source, destination)
  location = getLocation place, lang
  unless location
    reply 'yr.no came up "wah?"'
  whereabouts = {location.name}
  category = location.category and location.category.name
  table.insert whereabouts, category if category
  subregion = location.subregion and location.subregion.name
  table.insert whereabouts, subregion if subregion
  region = location.region and location.region.name
  table.insert whereabouts, region if region
  country = location.country.name
  table.insert whereabouts, country

  whereabouts = table.concat whereabouts, ', '

  f_link = apiBase .. location._links.forecast.href
  f_data = simplehttp f_link
  forecast = json.decode f_data

  _, now = next forecast.shortIntervals

  temp = now.temperature.value
  feelsLike = ''
  if now.feelsLike and now.feelsLike.value
    the_feels = now.feelsLike.value
    feels_word = 'feels like'
    if lang\match '^n'
      feels_word = 'følt'
    feelsLike = "(#{feels_word} #{color(the_feels)(the_feels)} °C) "

  windDirection = now.wind.direction
  windDirectionarrow = arrow deg2name windDirection
  windSpeed = now.wind.speed
  windSpeedname = windSpeedToName windSpeed, lang

  cloudCover = now.cloudCover.value
  cloudCovername = 'cloud cover'
  if lang\match '^n'
    cloudCovername = 'skydekke'

  say '%s °C, %s%s%% %s, %s %s (%s m/s) (%s)', color(temp)(temp), feelsLike, cloudCover, cloudCovername, windDirectionarrow, windSpeedname, windSpeed, whereabouts

PRIVMSG:
  '^%pbtemp%s*(.*)$': handleTempLookup
  '^%pdeg (.*)$': (s, d, a) =>
    say deg2name(tonumber(a))

error: pathspec 'modules/betayr.moon' did not match any file(s) known to git


utf8: new module

utf8: new module

Former-commit-id: 1a48d394980e86ac175c5a8f0c60961b16bd9ef5 [formerly 53b413dad6c77b4240397dfc3b43fc854a3db973]
Former-commit-id: 6168bbc274c8ee2344036f6b15186584559ba12b

html2unicode = require'html'

replace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      if bc == 32
        t[#t + 1] = '\227\128\128'
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

wide = (source, destination, arg) =>
  @Msg 'privmsg', destination, source, replace(0xFEE0, arg)

blackletter = (source, destination, arg) =>
  @Msg 'privmsg', destination, source, replace(0x1D4A3, arg)

PRIVMSG:
  '^%pwide (.+)$': wide
  '^%pblackletter (.+)$': blackletter

error: pathspec 'modules/utf8.moon' did not match any file(s) known to git


utf8: new module

utf8: new module


logger: log incoming messages to postgresql

dbi = require 'DBI'
require'logging.console'
log = logging.console()

conn = false

schemas = {[[
  CREATE TABLE IF NOT EXISTS log (
    time timestamp DEFAULT now(),
    nick text,
    channel text,
    message text,
    type text
  );]],
  [[CREATE INDEX idx_time ON log(time);]],
  [[CREATE INDEX idx_channel ON log(channel);]]
}

connect = ->
  conn, err = DBI.Connect('PostgreSQL', ivar2.config.dbname, ivar2.config.dbuser, ivar2.config.dbpass, ivar2.config.dbhost, ivar2.config.dbport)
  unless conn
    log\error "Unable to connect to DB: #{err}"
    return

  conn\autocommit(true)

  --for s in *schemas
  --  a,b = DBI.Do(conn, s)

dbh = ->
  connect! unless conn

  -- Check if connection is alive
  alive = conn\ping!
  connect! unless alive

  success, err = DBI.Do(conn, 'SELECT NOW()')
  unless success
    log\error "SQL Connection :#{err}"
    connect!

  return conn

dblog = (type, source, destination, arg) =>
  nick = source.nick

  if type == 'PRIVMSG'
    -- action
    if arg\sub(1,1) == '\001' and arg\sub(-1) == '\001'
      arg = arg\sub(9, -2)
    type = 'ACTION'

  unless arg
    arg = ''

  insert = dbh!\prepare('INSERT INTO log(nick,channel,message,type) values(?,?,?,?)')
  stmt, err = insert\execute(nick, destination, arg, type)
  unless stmt
    log\error err

return {
  PRIVMSG: {
    (s, d, a) =>
      dblog @, 'PRIVMSG', s, d, a
  }
  NOTICE: {
    (s, d, a) =>
      dblog @, 'NOTICE', s, d, a
  }
  JOIN: {
    (s, d, a) =>
      dblog @, 'JOIN', s, d, a
  }
  PART: {
    (s, d, a) =>
      dblog @, 'PART', s, d, a
  }
  KICK: {
    (s, d, a) =>
      dblog @, 'KICK', s, d, a
  }
  MODE: {
    (s, d, a) =>
      dblog @, 'MODE', s, d, a
  }
  TOPIC: {
    (s, d, a) =>
      dblog @, 'TOPIC', s, d, a
  }
  NICK: {
    (s, d, a) =>
      dblog @, 'NICK', s, d, a
  }
  QUIT: {
    (s, d, a) =>
      dblog @, 'QUIT', s, d, a
  }
}

error: pathspec 'modules/logger.moon' did not match any file(s) known to git


stats: new module to display some stats about the bot

--- Stats about the bot

util = require'util'

-- hardcoded, update as you see fit
hz = 250

--- Line named field reader
lineparser = (fname, fields) ->
  vals = {}
  fd = io.open(fname, 'r')
  data = fd\read'*a'
  fd\close!
  i = 1
  for field in data\gmatch '(.-) '
    -- lazy attempt
    val = tonumber(field) or field
    fname = fields[i] or i
    vals[fname] = val
    i = i +1
  return vals

--- Get all them stats
stats = (source, dest) =>

  vals = lineparser '/proc/self/stat', {'pid', 'comm', 'state', 'ppid', 'pgrp', 'session', 'tty_nr', 'tpgid', 'flags', 'minflt', 'cminflt', 'majflt', 'cmajflt', 'utime', 'stime', 'cutime', 'cstime', 'priority', 'nice', 'num_threads', 'itrealvalue', 'starttime', 'vsize', 'rss', 'rsslim', 'startcode', 'endcode', 'startstack', 'kstkesp', 'kstkeip', 'signal', 'blocked', 'siginore', 'sigcatch', 'wchan', 'nswap', 'cnswap', 'exit_signal', 'processor', 'rt_priority', 'policy', 'delayacct_blkio_ticks', 'guest_time', 'cguest_time', 'start_data', 'end_data', 'start_brk', 'arg_start', 'arg_end', 'env_start', 'env_end', 'exit_codei'}

  local cpu_s
  with vals
    uptime = math.floor .starttime/hz/86400
    cpu_s = "Uptime: #{uptime} days, user time: #{.utime/hz}, system time: #{.stime/hz}"

  mvals = lineparser '/proc/self/statm', {'size', 'resident', 'share', 'text', 'lib', 'data', 'dt'}
  mem_s = "Mem usage: #{math.floor((mvals.size - mvals.resident - mvals.share)/1024)}M"

  modules = 0
  commands = 0
  for moduleName, moduleTable in pairs @Events!['PRIVMSG']
    modules += 1
    for patt, cb in pairs(moduleTable)
      commands += 1
  mod_s = "#{modules} modules loaded totalling #{commands} commands"
  say "#{cpu_s}, #{mem_s}, #{mod_s}"

'PRIVMSG':
  '^%pstats$': stats

error: pathspec 'modules/stats.moon' did not match any file(s) known to git


trigger: first version

trigger: first version

Former-commit-id: cefdd0f3542e81d1fcae4dea674ea7d7f14819bf [formerly b4f75395aa3215953a65f93815affd64d475d158]
Former-commit-id: 86809f32ab4cab275a25a2c0c10a1ee96de2e4b1

sql = require'lsqlite3'

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

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

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

-- Construct a safe environ for trigger with two functions; say and reply
--- TODO: actually make it safe :-)
--- 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)

-- Stub to create a trigger name in event table
handlerName = (pattern) ->
  "trigger#{pattern}"

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

  @RegisterCommand handlerName(pattern), pattern, triggerHandler(@, source, destination, funcstr)

delCommand = (source, destination, pattern) =>
  db = openDb!
  ins = db\prepare "DELETE FROM trigger WHERE pattern = ?"
  code = ins\bind_values pattern
  code = ins\step!
  code = ins\finalize!
  db\close!
  @UnregisterCommand handlerName(pattern)

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

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

error: pathspec 'modules/trigger.moon' did not match any file(s) known to git


anagram: new module

anagram: new module


draft of a gist getter (lua needs to be deployed to pastebin)

if not http
    print "get requires the http API"
    print "Set enableAPI_http to 1 in mod_ComputerCraft.cfg"

requested = arg[1]

os.loadAPI 'gist_map'

downloaded = {}

for file, url in pairs(gist_map.url_table!)
    if requested == 'all' or file == requested
        print "Retrieving #{file} from #{url}/raw"
        lua_code = http.get "#{url}/raw"
        unless lua_code
            print "!! Unable to retrieve contents of #{file} !!"
            return
        fh = fs.open file, "w"
        fh.write lua_code.readAll()
        fh.close!
        lua_code.close!
        table.insert downloaded, file
        print "Remote file \"#{file}\" downloaded successfully!"

print "Successfully downloaded #{#downloaded} files."

error: pathspec 'scripts/get.moon' did not match any file(s) known to git


Added math.Projection

Added math.Projection

export ^

class Projection
	new: =>
		@max, @min = 0, 0

	-- Check if two projection overlap.
	--
	-- @param	other	The other projection
	--
	-- @return	If they overlap.
	overlaps: (other) => not (@min > other.max or @max < other.min)

	-- Get the value of two projection overlap.
	--
	-- @param	other	The other projection
	--
	-- @return	The overlap value.
	getOverlap: (other) => if @max > other.max
			@max - other.min
		else
			other.max - @min

	-- Prints the projection as a string
	--
	-- @return	String version of the object.
	toString: => string.format "[%f, %f]", @min, @max

	__tostring: => @toString!

error: pathspec 'LunoPunk/math/Projection.moon' did not match any file(s) known to git


sun/lexer/literal: add initial literals

sun/lexer/literal: add initial literals

Literal items added:

 * Space
 * Whitespace
 * EndOfLine
 * Shebang
 * Comment
 * Number
 * Upper
 * Lower
 * Numeric
 * Class
 * Variable
 * Macro
 * SingleQuote
 * DoubleQuote
 * FullEscape
 * SingleQuoteString
 * DoubleQuoteString
 * String

-- vim:set noet sts=0 sw=3 ts=3:

import P, S, R from require "lpeg"

-- "Ignored" patterns

Space      = S" "
Whitespace = S" \t\r\n\v" ^ 0
EndOfLine  = P"\r" ^ -1 * P"\n"
Shebang    = P"#!" * P(1 - EndOfLine) ^ 0 * EndOfLine
Comment    = P"--" * P(1 - EndOfLine) ^ 0 * EndOfLine

-- Number pattern

Number = P"0x" * R("09", "af", "AF") ^ 1 * (S"uU" ^ -1 * S"lL" ^ 2) ^ -1 +
	R"09" ^ 1 * (S"uU" ^ -1 * S"lL" ^ 2) +
	(
		R"09" ^ 1 * (P"." * R"09" ^ 1) ^ -1 +
		P"." * R"09" ^ 1
	) * (S"eE" * P"-" ^ -1 * R"09" ^ 1) ^ -1

-- Names

Upper   = R"AZ"
Lower   = R"az"
Letter  = Upper + Lower
Numeric = R"09"

Class    = Upper * Letter ^ 0
Variable = Lower ^ 1
Macro    = Upper ^ 1

-- Strings

-- ::TODO:: single strings don't escape with \, double strings do

SingleQuote = P"'"
DoubleQuote = P'"'
FullEscape  = P"\\" * 1 -- backslash followed by something else

SingleQuoteString = SingleQuote * (1 - SingleQuote) ^ 0 * SingleQuote
DoubleQuoteString = DoubleQuote * (FullEscape + (1 - (P"\\" + DoubleQuote))) ^ 0 * DoubleQuote

String = SingleQuoteString + DoubleQuoteString

-- ::TODO:: add Table, TableKey, etc.

return {
	:Space, :Whitespace, :EndOfLine, :Shebang, :Comment,
	:Number,
	:Upper, :Lower, :Letter, :Numeric,
	:Class, :Variable, :Macro,
	:SingleQuote, :DoubleQuote, :FullEscape,
	:SingleQuoteString, :DoubleQuoteString,
	:String
}

error: pathspec 'sun/lexer/literal.moon' did not match any file(s) known to git


translate: new module, (ab)use google translate

Former-commit-id: 9d16a3f1f659d0e9af9abc1b7f982e3c57592708 [formerly 4a6a8c6a42458c8c38f6c946f89b013d84d7fdc5]
Former-commit-id: 7cedefb8cdd26794e6a522f5339e6244def74219

-- google translate
json = require'json'
simplehttp = require'simplehttp'

languages =
  "af": "Afrikaans",
  "sq": "Albanian",
  "ar": "Arabic",
  "az": "Azerbaijani",
  "eu": "Basque",
  "bn": "Bengali",
  "be": "Belarusian",
  "bg": "Bulgarian",
  "ca": "Catalan",
  "zh-CN": "Simplified Chinese",
  "zh-TW": "Traditional Chinese",
  "hr": "Croatian",
  "cs": "Czech",
  "da": "Danish",
  "nl": "Dutch",
  "en": "English",
  "eo": "Esperanto",
  "et": "Estonian",
  "tl": "Filipino",
  "fi": "Finnish",
  "fr": "French",
  "gl": "Galician",
  "ka": "Georgian",
  "de": "German",
  "el": "Greek",
  "gu": "Gujarati",
  "ht": "Haitian Creole",
  "iw": "Hebrew",
  "hi": "Hindi",
  "hu": "Hungarian",
  "is": "Icelandic",
  "id": "Indonesian",
  "ga": "Irish",
  "it": "Italian",
  "ja": "Japanese",
  "kn": "Kannada",
  "ko": "Korean",
  "la": "Latin",
  "lv": "Latvian",
  "lt": "Lithuanian",
  "mk": "Macedonian",
  "ms": "Malay",
  "mt": "Maltese",
  "no": "Norwegian",
  "fa": "Persian",
  "pl": "Polish",
  "pt": "Portuguese",
  "ro": "Romanian",
  "ru": "Russian",
  "sr": "Serbian",
  "sk": "Slovak",
  "sl": "Slovenian",
  "es": "Spanish",
  "sw": "Swahili",
  "sv": "Swedish",
  "ta": "Tamil",
  "te": "Telugu",
  "th": "Thai",
  "tr": "Turkish",
  "uk": "Ukrainian",
  "ur": "Urdu",
  "vi": "Vietnamese",
  "cy": "Welsh",
  "yi": "Yiddish"

getCode = (language) ->
  for code, lang in pairs languages
    if lang\lower! == language\lower!
      return code
    else if language\lower! == code\lower!
      return code

urlEncode = (str, space) ->
  space = space or '+'
  str = tostring(str)

  str = str\gsub '([^%w ])', (c) ->
    string.format  "%%%02X", string.byte(c)
  return str\gsub(' ', space)

buildQueryStr = (tbl) ->
  out = {}
  for k,v in pairs tbl
    table.insert out, k..'='..urlEncode(v)
  table.concat out, '&'

translate = (source, destination, origin, target, term) =>
  origin = getCode(origin) or 'auto'
  target = getCode(target) or 'en'
  args =
    client: 'p'
    hl: 'en'
    multires: 1
    sc: 1
    sl: origin
    ssel: 0
    tl: target
    tsel: 0
    uptl: "en"
    text: term

  simplehttp "https://translate.google.com/translate_a/t?" .. buildQueryStr(args), (data) ->
    if #data > 4
      parsed = json.decode data
      language = parsed.src
      out = table.concat [k.trans for k in *parsed.sentences]
      if #out > 0
        @Msg 'privmsg', destination, source, "\002#{languages[target]}\002: #{out}"

PRIVMSG:
  '^%ptranslate ([%w%-]+) ([%w%-]+) (.*)$': translate

error: pathspec 'modules/translate.moon' did not match any file(s) known to git


prisguiden: new module

prisguiden: new module

util = require'util'
simplehttp = util.simplehttp
json = util.json
urlEncode = util.urlEncode

siValue = (val) ->
  if val >= 1e6
    return ('%.1f')\format(val / 1e6)\gsub('%.', 'M')\gsub('M0', 'M')
  elseif(val >= 1e4) then
    return ("%.1f")\format(val / 1e3)\gsub('%.', 'k')\gsub('k0', 'k')
  else
   return val

lookup = (s, d, a) =>
  data = simplehttp "http://prisguiden.no/sok?module=PGBridgeService&service=getData&providers%5B%5D%3D=searchResults&q=#{urlEncode a}&o=0&l=1"
  unless data
    say 'No reply from API'
  data = json.decode(data)
  unless data
    say 'Invalid json'
    return
  -- get the first search result
  res = data['searchResults']['products'][1]

  price = res.price
  if price > 10000
    price = siValue price
  price ..= ' kr'

  offer = ''
  if res.ratio and res.ratio != json.null
    if res.ratio < 0
      offer = " " ..util.green(tostring(res.ratio .. ' %'))

  priceCount = res.priceCount
  if priceCount > 1
    priceCount ..= ' prisar'
  else
    priceCount ..= ' pris'

  out = {}
  out[#out+1] = res.title
  out[#out+1] = '[ '.. price..' ]'
  out[#out+1] = offer
  out[#out+1] = priceCount
  if res.productId
    out[#out+1] = "http://pris.guide/produkt/" .. res.productId
  else
    out[#out+1] = "http://pris.guide" .. res.url

  say table.concat(out, ' ')

PRIVMSG:
  '^%ppg (.*)$': lookup
  '^%pprisuiden (.*)$': lookup

error: pathspec 'modules/prisguiden.moon' did not match any file(s) known to git


navnedag: module for norwegian name days

Former-commit-id: 35a0ea507faab2e5abdd5ed472ecf048e642a7c6 [formerly c638708dcfba1a6ad05a3172e0d2a75fcfb5d4f9]
Former-commit-id: cba87f2c278cf4594f17f3962a82650091dd88e7

os = require'os'
util = require'util'
data =
  "01.01":"Jesus"
  "02.01":"Dagfinn Dagfrid"
  "03.01":"Alfred Alf"
  "04.01":"Roar Roger"
  "05.01":"Hanna Hanne"
  "06.01":"Aslaug Åslaug"
  "07.01":"Eldbjørg Knut"
  "08.01":"Torfinn Turid"
  "09.01":"Gunnar Gunn"
  "10.01":"Sigmund Sigrun"
  "11.01":"Børge Børre"
  "12.01":"Reinhard Reinert"
  "13.01":"Gisle Gislaug"
  "14.01":"Herbjørn Herbjørg"
  "15.01":"Laurits Laura"
  "16.01":"Hjalmar Hilmar"
  "17.01":"Anton Tønnes Tony"
  "18.01":"Hildur Hild"
  "19.01":"Marius Margunn"
  "20.01":"Fabian Sebastian Bastian"
  "21.01":"Agnes Agnete"
  "22.01":"Ivan Vanja"
  "23.01":"Emil Emilie Emma"
  "24.01":"Joar Jarle Jarl"
  "25.01":"Paul Pål"
  "26.01":"Øystein Esten"
  "27.01":"Gaute Gurly Gry"
  "28.01":"Karl Karoline"
  "29.01":"Herdis Hermod Hermann"
  "30.01":"Gunnhild Gunda"
  "31.01":"Idun Ivar"
  "01.02":"Birte Bjarte"
  "02.02":"Jomar Jostein"
  "03.02":"Ansgar Asgeir"
  "04.02":"Veronika Vera"
  "05.02":"Agate Ågot"
  "06.02":"Dortea Dorte"
  "07.02":"Rikard Rigmor Riborg"
  "08.02":"Åshild Åsne"
  "09.02":"Lone Leikny"
  "10.02":"Ingfrid Ingrid"
  "11.02":"Ingve Yngve"
  "12.02":"Randulf Randi Ronja"
  "13.02":"Svanhild Svanlaug"
  "14.02":"Hjørdis Jardar"
  "15.02":"Sigfred Sigbjørn"
  "16.02":"Julian Juliane Jill"
  "17.02":"Aleksandra Sandra Sondre"
  "18.02":"Frøydis Frode"
  "19.02":"Ella Elna"
  "20.02":"Halldis Halldor"
  "21.02":"Samuel Selma Celine"
  "22.02":"Tina Tine Tim"
  "23.02":"Torstein Torunn"
  "24.02":"Mattias Mattis Mats"
  "25.02":"Viktor Viktoria"
  "26.02":"Inger Ingjerd"
  "27.02":"Laila Lill"
  "28.02":"Marina Maren"
  "01.03":"Audny Audun"
  "02.03":"Erna Ernst"
  "03.03":"Gunnbjørg Gunnveig"
  "04.03":"Ada Adrian"
  "05.03":"Patrick Patricia"
  "06.03":"Annfrid Andor"
  "07.03":"Arild Are"
  "08.03":"Beate Betty Bettina"
  "09.03":"Sverre Sindre"
  "10.03":"Edel Edle"
  "11.03":"Edvin Tale"
  "12.03":"Gregor Gro"
  "13.03":"Greta Grete"
  "14.03":"Mathilde Mette"
  "15.03":"Christel Christer Chris"
  "16.03":"Gudmund Gudny"
  "17.03":"Gjertrud Trude"
  "18.03":"Aleksander Sander Edvard"
  "19.03":"Josef Josefine"
  "20.03":"Joakim Kim"
  "21.03":"Bendik Bengt Bent"
  "22.03":"Paula Pauline"
  "23.03":"Gerda Gerd"
  "24.03":"Ulrikke Rikke"
  "25.03":"Maria Marie Mari"
  "26.03":"Gabriel Glenn"
  "27.03":"Rudolf Rudi"
  "28.03":"Åsta Åste"
  "29.03":"Jonas Jonatan"
  "30.03":"Holger Olga"
  "31.03":"Vebjørn Vegard"
  "01.04":"Aron Arve Arvid"
  "02.04":"Sigvard Sivert"
  "03.04":"Gunnvald Gunnvor"
  "04.04":"Nanna Nancy Nina"
  "05.04":"Irene Eirin Eril"
  "06.04":"Åsmund Asmund"
  "07.04":"Oddveig Oddvin"
  "08.04":"Asle Atle"
  "09.04":"Rannveig Rønnaug"
  "10.04":"Ingvald Ingveig"
  "11.04":"Ylva Ulf"
  "12.04":"Julius Julie"
  "13.04":"Asta Astrid"
  "14.04":"Ellinor Nora"
  "15.04":"Oda Odin Odd"
  "16.04":"Magnus Mons"
  "17.04":"Elise Else Elsa"
  "18.04":"Eilen Eilert"
  "19.04":"Arnfinn Arnstein"
  "20.04":"Kjellaug Kjellrun"
  "21.04":"Jeanette Jannike"
  "22.04":"Oddgeir Oddny"
  "23.04":"Georg Jørgen Jørg"
  "24.04":"Albert Olaug"
  "25.04":"Markus Mark"
  "26.04":"Terese Tea"
  "27.04":"Charles Charlotte Lotte"
  "28.04":"Vivi Vivian"
  "29.04":"Toralf Torolf"
  "30.04":"Gina Gitte"
  "01.05":"Filip Valborg"
  "02.05":"Åsa Åse"
  "03.05":"Gjermund Gøril"
  "04.05":"Monika Mona"
  "05.05":"Gudbrand Gullborg"
  "06.05":"Guri Gyri"
  "07.05":"Maia Mai Maiken"
  "08.05":"Åge Åke"
  "09.05":"Kasper Jesper"
  "10.05":"Asbjørg Asbjørn Espen"
  "11.05":"Magda Malvin"
  "12.05":"Normann Norvald"
  "13.05":"Linda Line Linn"
  "14.05":"Kristian Kristen Karsten"
  "15.05":"Hallvard Hallvor"
  "16.05":"Sara Siren"
  "17.05":"Harald Ragnhild"
  "18.05":"Eirik Erik Erika"
  "19.05":"Torjus Torje Truls"
  "20.05":"Bjørnar Bjørnhild"
  "21.05":"Helene Ellen Eli"
  "22.05":"Henning Henny"
  "23.05":"Oddleif Oddlaug"
  "24.05":"Ester Iris"
  "25.05":"Ragna Ragnar"
  "26.05":"Annbjørg Annlaug"
  "27.05":"Katinka Cato"
  "28.05":"Vilhelm William Willy"
  "29.05":"Magnar Magnhild"
  "30.05":"Gard Geir"
  "31.05":"Pernille Preben"
  "01.06":"June Juni"
  "02.06":"Runa Runar Rune"
  "03.06":"Rasmus Rakel"
  "04.06":"Heidi Heid"
  "05.06":"Torbjørg Torbjørn Torben"
  "06.06":"Gustav Gyda"
  "07.06":"Robert Robin"
  "08.06":"Renate René"
  "09.06":"Kolbein Kolbjørn"
  "10.06":"Ingolf Ingunn"
  "11.06":"Borgar Bjørge Bjørg"
  "12.06":"Sigfrid Sigrid Siri"
  "13.06":"Tone Tonje Tanja"
  "14.06":"Erlend Erland"
  "15.06":"Vigdis Viggo"
  "16.06":"Torhild Toril Tiril"
  "17.06":"Botolf Bodil"
  "18.06":"Bjarne Bjørn"
  "19.06":"Erling Elling"
  "20.06":"Salve Sølve Sølvi"
  "21.06":"Agnar Annar"
  "22.06":"Håkon Maud"
  "23.06":"Elfrid Eldrid"
  "24.06":"Johannes Jon Hans"
  "25.06":"Jørund Jorunn"
  "26.06":"Jenny Jonny"
  "27.06":"Aina Ina Ine"
  "28.06":"Lea Leo Leon"
  "29.06":"Peter Petter Per"
  "30.06":"Solbjørg Solgunn"
  "01.07":"Ask Embla"
  "02.07":"Kjartan Kjellfrid"
  "03.07":"Andrea Andrine André"
  "04.07":"Ulrik Ulla"
  "05.07":"Mirjam Mina"
  "06.07":"Torgrim Torgunn"
  "07.07":"Håvard Hulda"
  "08.07":"Sunniva Synnøve Synne"
  "09.07":"Gøran Jøran Ørjan"
  "10.07":"Anita Anja"
  "11.07":"Kjetil Kjell"
  "12.07":"Elias Eldar"
  "13.07":"Mildrid Melissa Mia"
  "14.07":"Solfrid Solrun"
  "15.07":"Oddmund Oddrun"
  "16.07":"Susanne Sanna"
  "17.07":"Guttorm Gorm"
  "18.07":"Arnulf Ørnulf"
  "19.07":"Gerhard Gjert"
  "20.07":"Margareta Margit Marit"
  "21.07":"Johanne Janne Jane"
  "22.07":"Malene Malin Mali"
  "23.07":"Brita Brit Britt"
  "24.07":"Kristine Kristin Kristi"
  "25.07":"Jakob Jack Jim"
  "26.07":"Anna Anne Ane"
  "27.07":"Marita Rita"
  "28.07":"Reidar Reidun"
  "29.07":"Olav Ola Ole"
  "30.07":"Aurora Audhild Aud"
  "31.07":"Elin Eline"
  "01.08":"Peder Petra"
  "02.08":"Karen Karin"
  "03.08":"Oline Oliver Olve"
  "04.08":"Arnhild Arna Arnt"
  "05.08":"Osvald Oskar"
  "06.08":"Gunnlaug Gunnleiv"
  "07.08":"Didrik Doris"
  "08.08":"Evy Yvonne"
  "09.08":"Ronald Ronny"
  "10.08":"Lorents Lars Lasse"
  "11.08":"Torvald Tarald"
  "12.08":"Klara Camilla"
  "13.08":"Anny Anine Ann"
  "14.08":"Hallgeir Hallgjerd"
  "15.08":"Margot Mary Marielle"
  "16.08":"Brynjulf Brynhild"
  "17.08":"Verner Wenche"
  "18.08":"Tormod Torodd"
  "19.08":"Sigvald Sigve"
  "20.08":"Bernhard Bernt"
  "21.08":"Ragnvald Ragni"
  "22.08":"Harriet Harry"
  "23.08":"Signe Signy"
  "24.08":"Belinda Bertil"
  "25.08":"Ludvig Lovise Louise"
  "26.08":"Øyvind Eivind Even"
  "27.08":"Roald Rolf"
  "28.08":"Artur August"
  "29.08":"Johan Jone Jo"
  "30.08":"Benjamin Ben"
  "31.08":"Berta Berte"
  "01.09":"Solveig Solvår"
  "02.09":"Lisa Lise Liss"
  "03.09":"Alise Alvhild Vilde"
  "04.09":"Ida Idar"
  "05.09":"Brede Brian Njål"
  "06.09":"Sollaug Siril Siv"
  "07.09":"Regine Rose"
  "08.09":"Amalie Alma Allan"
  "09.09":"Trygve Tyra Trym"
  "10.09":"Tord Tor"
  "11.09":"Dagny Dag"
  "12.09":"Jofrid Jorid"
  "13.09":"Stian Stig"
  "14.09":"Ingebjørg Ingeborg"
  "15.09":"Aslak Eskil"
  "16.09":"Lillian Lilly"
  "17.09":"Hildebjørg Hildegunn"
  "18.09":"Henriette Henry"
  "19.09":"Konstanse Connie"
  "20.09":"Tobias Tage"
  "21.09":"Trine Trond"
  "22.09":"Kyrre Kåre"
  "23.09":"Snorre Snefrid"
  "24.09":"Jan Jens"
  "25.09":"Ingvar Yngvar"
  "26.09":"Einar Endre"
  "27.09":"Dagmar Dagrun"
  "28.09":"Lena Lene"
  "29.09":"Mikael Mikal Mikkel"
  "30.09":"Helga Helge Hege"
  "01.10":"Rebekka Remi"
  "02.10":"Live Liv"
  "03.10":"Evald Evelyn"
  "04.10":"Frans Frank"
  "05.10":"Brynjar Boye Bo"
  "06.10":"Målfrid Møyfrid"
  "07.10":"Birgitte Birgit Berit"
  "08.10":"Benedikte Bente"
  "09.10":"Leidulf Leif"
  "10.10":"Fridtjof Frida Frits"
  "11.10":"Kevin Kennet Kent"
  "12.10":"Valter Vibeke"
  "13.10":"Torgeir Terje Tarjei"
  "14.10":"Kaia Kai"
  "15.10":"Hedvig Hedda"
  "16.10":"Flemming Finn"
  "17.10":"Marta Marte"
  "18.10":"Kjersti Kjerstin"
  "19.10":"Tora Tore"
  "20.10":"Henrik Heine Henrikke"
  "21.10":"Bergljot Birger"
  "22.10":"Karianne Karine Kine"
  "23.10":"Severin Søren"
  "24.10":"Eilif Eivor"
  "25.10":"Margrete Merete Märtha"
  "26.10":"Amandus Amanda"
  "27.10":"Sturla Sture"
  "28.10":"Simon Simen"
  "29.10":"Noralf Norunn"
  "30.10":"Aksel Åmund Ove"
  "31.10":"Edit Edna"
  "01.11":"Veslemøy Vetle"
  "02.11":"Tove Tuva"
  "03.11":"Raymond Roy"
  "04.11":"Otto Ottar"
  "05.11":"Egil Egon"
  "06.11":"Leonard Lennart"
  "07.11":"Ingebrigt Ingelin"
  "08.11":"Ingvild Yngvild"
  "09.11":"Tordis Teodor"
  "10.11":"Gudbjørg Gudveig"
  "11.11":"Martin Morten Martine"
  "12.11":"Torkjell Torkil"
  "13.11":"Kirsten Kirsti"
  "14.11":"Fredrik Fred Freddy"
  "15.11":"Oddfrid Oddvar"
  "16.11":"Edmund Edgar"
  "17.11":"Hugo Hogne Hauk"
  "18.11":"Magne Magny"
  "19.11":"Elisabet Lisbet"
  "20.11":"Halvdan Helle"
  "21.11":"Mariann Marianne"
  "22.11":"Cecilie Silje Sissel"
  "23.11":"Klement Klaus"
  "24.11":"Gudrun Guro"
  "25.11":"Katarina Katrine Kari"
  "26.11":"Konrad Kurt"
  "27.11":"Torlaug Torleif"
  "28.11":"Ruben Rut"
  "29.11":"Sofie Sonja"
  "30.11":"Andreas Anders"
  "01.12":"Arnold Arnljot Arnt"
  "02.12":"Borghild Borgny Bård"
  "03.12":"Sveinung Svein"
  "04.12":"Barbara Barbro"
  "05.12":"Stine Ståle"
  "06.12":"Nils Nikolai"
  "07.12":"Hallfrid Hallstein"
  "08.12":"Marlene Marion Morgan"
  "09.12":"Anniken Annette"
  "10.12":"Judit og Jytte"
  "11.12":"Daniel Dan"
  "12.12":"Pia Peggy"
  "13.12":"Lucia Lydia"
  "14.12":"Steinar Stein"
  "15.12":"Hilda Hilde"
  "16.12":"Oddbjørg Oddbjørn"
  "17.12":"Inga Inge"
  "18.12":"Kristoffer Kate"
  "19.12":"Iselin Isak"
  "20.12":"Abraham Amund"
  "21.12":"Tomas Tom Tommy"
  "22.12":"Ingemar Ingar"
  "23.12":"Sigurd Sjur"
  "24.12":"Adam Eva"
  "25.12":"Ingen navnedag"
  "26.12":"Stefan Steffen"
  "27.12":"Narve Natalie"
  "28.12":"Unni Une Unn"
  "29.12":"Vidar Vemund"
  "30.12":"David Diana Dina"
  "31.12":"Sylfest Sylvia Sylvi"

PRIVMSG:
  "^%pnavnedag$": (source, destination) =>
    now = os.date'%d.%m'
    names = table.concat(util.split(data[now], ' '), ', ')
    say names
  "^%pnavnedag (%d+).(%d+)$": (source, destination, day, month) =>
    day = string.format('%02d', day)
    month = string.format('%02d', month)
    now = day .. '.' .. month
    names = table.concat(util.split(data[now], ' '), ', ')
    say names

error: pathspec 'modules/navnedag.moon' did not match any file(s) known to git


emoji: new module to print emojis

emoji: new module to print emojis

emojis =
  emoticons:'😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏
    😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟
    😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮😯
    😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿
    🙀🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏',
  transport:'🚀🚁🚂🚃🚄🚅🚆🚇🚈🚉🚊🚋🚌🚍🚎🚏
    🚐🚑🚒🚓🚔🚕🚖🚗🚘🚙🚚🚛🚜🚝🚞🚟
    🚠🚡🚢🚣🚤🚥🚦🚧🚨🚩🚪🚫🚬🚭🚮🚯
    🚰🚱🚲🚳🚴🚵🚶🚷🚸🚹🚺🚻🚼🚽🚾🚿
    🛀🛁🛂🛃🛄🛅',
  symbol:'☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏
    ☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟
    ☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯
    ☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿
    ♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏
    ♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟
    ♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯
    ♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿
    ⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏
    ⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟
    ⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯
    ⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿
    ⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏
    ⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟
    ⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯
    ⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿',
  misc:'🌀🌁🌂🌃🌄🌅🌆🌇🌈🌉🌊🌋🌌🌍🌎🌏
    🌐🌑🌒🌓🌔🌕🌖🌗🌘🌙🌚🌛🌜🌝🌞🌟
    🌠
    🌰🌱🌲🌳🌴🌵🌷🌸🌹🌺🌻🌼🌽🌾🌿
    🍀🍁🍂🍃🍄🍅🍆🍇🍈🍉🍊🍋🍌🍍🍎🍏
    🍐🍑🍒🍓🍔🍕🍖🍗🍘🍙🍚🍛🍜🍝🍞🍟
    🍠🍡🍢🍣🍤🍥🍦🍧🍨🍩🍪🍫🍬🍭🍮🍯
    🍰🍱🍲🍳🍴🍵🍶🍷🍸🍹🍺🍻🍼
    🎀🎁🎂🎃🎄🎅🎆🎇🎈🎉🎊🎋🎌🎍🎎🎏
    🎐🎑🎒🎓
    🎠🎡🎢🎣🎤🎥🎦🎧🎨🎩🎪🎫🎬🎭🎮🎯
    🎰🎱🎲🎳🎴🎵🎶🎷🎸🎹🎺🎻🎼🎽🎾🎿
    🏀🏁🏂🏃🏄🏆🏇🏈🏉🏊

    🏠🏡🏢🏣🏤🏥🏦🏧🏨🏩🏪🏫🏬🏭🏮🏯
    🏰
    🐀🐁🐂🐃🐄🐅🐆🐇🐈🐉🐊🐋🐌🐍🐎🐏
    🐐🐑🐒🐓🐔🐕🐖🐗🐘🐙🐚🐛🐜🐝🐞🐟
    🐠🐡🐢🐣🐤🐥🐦🐧🐨🐩🐪🐫🐬🐭🐮🐯
    🐰🐱🐲🐳🐴🐵🐶🐷🐸🐹🐺🐻🐼🐽🐾
    👀👂👃👄👅👆👇👈👉👊👋👌👍👎👏
    👐👑👒👓👔👕👖👗👘👙👚👛👜👝👞👟
    👠👡👢👣👤👥👦👧👨👩👪👫👬👭👮👯
    👰👱👲👳👴👵👶👷👸👹👺👻👼👽👾👿
    💀💁💂💃💄💅💆💇💈💉💊💋💌💍💎💏
    💐💑💒💓💔💕💖💗💘💙💚💛💜💝💞💟
    💠💡💢💣💤💥💦💧💨💩💪💫💬💭💮💯
    💰💱💲💳💴💵💶💷💸💹💺💻💼💽💾💿
    📀📁📂📃📄📅📆📇📈📉📊📋📌📍📎📏
    📐📑📒📓📔📕📖📗📘📙📚📛📜📝📞📟
    📠📡📢📣📤📥📦📧📨📩📪📫📬📭📮📯
    📰📱📲📳📴📵📶📷📹📺📻📼
    🔀🔁🔂🔃🔄🔅🔆🔇🔈🔉🔊🔋🔌🔍🔎🔏
    🔐🔑🔒🔓🔔🔕🔖🔗🔘🔙🔚🔛🔜🔝🔞🔟
    🔠🔡🔢🔣🔤🔥🔦🔧🔨🔩🔪🔫🔬🔭🔮🔯
    🔰🔱🔲🔳🔴🔵🔶🔷🔸🔹🔺🔻🔼🔽
    🕀🕁🕂🕃
    🕐🕑🕒🕓🕔🕕🕖🕗🕘🕙🕚🕛🕜🕝🕞🕟
    🕠🕡🕢🕣🕤🕥🕦🕧
    🗻🗼🗽🗾🗿'

for cat, str in pairs emojis
  str, n = str\gsub('%s', '')
  emojis[cat] = str

PRIVMSG:
  '^%pemoji all$': (source, dest, arg) =>
    out = {}
    for cat, str in pairs emojis
      table.insert out, str
    @Msg 'privmsg', dest, source, table.concat out
  '^%pemoji$': (source, dest, arg) =>
    out = {}
    for cat, str in pairs emojis
      table.insert out, cat
    @Msg 'privmsg', dest, source, table.concat(out, ', ')
  '^%pemoji (.+)$': (source, dest, cat) =>
    @Msg 'privmsg', dest, source, emojis[cat]

error: pathspec 'modules/emoji.moon' did not match any file(s) known to git


svada: new module

svada: new module

-- svadagenerator.no/data/svadagenerator.csv

svada = {
  {"Gitt","en integrert","målsetting","synliggjøres","potensialet","innenfor rammen av","en samlet vurdering"},
  {"Under hensyntagen til","en optimal","effekt","tas det høyde for","risikofaktorene","som en følge av","forholdene"},
  {"I lys av","en sømløs","struktur","iverksettes","fokus","for så vidt gjelder","konseptet"},
  {"Vedrørende","en implisitt","agenda","identifiseres","synergieffekten","med henblikk på","ressurssituasjonen"},
  {"Grunnet","en proaktiv","tidshorisont","initieres","incitamentet","i forhold til","tilgjengeliggjøringen"},
  {"I betraktning av","en betydelig","overveielse","lokaliseres","forankringen","hva angår","føringene"},
  {"Forutsatt","en økt","mobilitet","kommuniseres","insentivene","parallelt med","evalueringen"},
  {"Med utgangspunkt i","en vesentlig","treffsikkerhet","styrkes","innsatsen","i relasjon til","implementeringen"},
  {"I forhold til","en ikke ubetydelig","innsats","realiseres","erfaringsutvekslingen","i tilknytning til","kjernevirksomheten"},
  {"Sett hen til","en kostnadseffektiv","kvalitetssikring","effektueres","informasjonsflyten","på bakgrunn av","visjonen"},
  {"I henhold til","en avtagende","problematikk","forankres","kriteriene","avhengig av","satsingsområdet"},
  {"Med tanke på","en vedvarende","ressursbruk","maksimeres","strategien","hva gjelder","problemstillingen"},
  {"Uavhengig av","en tiltagende","avveining","konkretiseres","økningen","eller sagt på en annen måte:","beskaffenheten"},
  {"Sett på bakgrunn av","en gjeldende","avklaring","tilgjengeliggjøres","egenarten","på samme måte som","vesentligheten"},
  {"Sammenholdt med","en helhetlig","implementering","utvides","tilstedeværelsen","i motsetning til","egenarten"},
  {"På grunn av","en manglende","styringsinnsats","dokumenteres","oppfølgingen","innenfor","målområdet"},
  {"Med hensyn til","en særlig","innovasjon","spores","resultatene","i tillegg til","verdiene"},
  {"Under forutsetning av","en løpende","effektivisering","innhentes","kunnskapene","gjennom","realitetsorienteringen"},
  {"Etter en totalvurdering av","en langsiktig","kvalitetsheving","revitaliseres","betydningen","ut fra","resultatoppnåelsen"},
  {"Uten hensyn til","en bærekraftig","utvikling","stabiliseres","kompetansehevingen","med sikte på","behovene"},
  {"Avhengig av","en resultatorientert","måloppnåelse","genereres","instrumentet","på tvers av","løsningen"},
  {"På grunnlag av","en tverrfaglig","oppgaveløsning","stimuleres","scenarioet","på linje med","parametrene"},
  {"I og med","en kommunikativ","arbeidsmodell","balanseres","spisskompetansen","utenom","ressursinnsatsen"},
  {"Under henvisning til","en inkluderende","organisasjon","ivaretas","relasjonene","i forlengelsen av","konsekvensaspektet"},
  {"Gitt","en plutselig","fornyelse","omfattes","fornyelsen","innenfor rammen av","oppgjøret"},
  {"Under hensyntagen til","en uforutsett","risikovurdering","utstedes","forsikringsbehovet","som en følge av","husstanden"},
  {"I lys av","en erstatningsmessig","forsikringstaker","iverksettes","vilkårene","under forutsetning av","årsakssammenhengen"},
  {"Vedrørende","en kollektiv","egenandel","anvendes","oppsigelsen","med henblikk på","betingelsene"},
  {"Grunnet","en indirekte","tariff","kommuniseres","angrefristen","i forhold til","opplysningsplikten"},
  {"I betraktning av","en skadevoldende","sikkerhetsforskrift","Innebæres","tegningstidspunktet","hva angår","behovsgjennomgangen"},
  {"Forutsatt","en særskilt","regress","tegnes","oppgjørsreglene","parallelt med","evalueringen"},
  {"Med utgangspunkt i","en ansvarsbegrensende","reassuranse","styrkes","klagenemden","i relasjon til","utredningsforløpet"},
  {"I forhold til","en inntruffet","utløpsdato","realiseres","vilkårstolkningen","i tilknytning til","kjernevirksomheten"},
  {"Sett hen til","en forholdsmessig","begunstigelse","baseres","løsøre","på bakgrunn av","visjonen"},
  {"I henhold til","en avvikende","helseerklæring","forankres","personopplysningene","avhengig av","satsingsområdet"},
  {"Med tanke på","en prosentvis","dekningsgrad","optimaliseres","konsekvensendringen","hva gjelder","porteføljen"},
  {"Uavhengig av","en juridisk","samlefaktura","konkretiseres","sviket","eller sagt på en annen måte:","lønnsomhetsmodellene"},
  {"Sett på bakgrunn av","en årlig","premie","meldes","flytteretten","på samme måte som","distribusjonskanalene"},
  {"Sammenholdt med","en ervervsmessig","indeksjustering","utvides","erstatningsansvaret","i motsetning til","avkortningen"},
  {"På grunn av","en bindende","rabatt","dokumenteres","segmentene","innenfor","takstmennene"},
  {"Med hensyn til","en prosessorientert","forutsetning","utredes","skadeskjemaene","i tillegg til","kjerneverdiene"},
  {"Under forutsetning av","en systemmessig","panthaver","innhentes","forsikrede","gjennom","skadeprosenten"},
  {"Etter en totalvurdering av","en skriftlig","sikkerhetsrabatt","ivaretas","premieberegningsgrunnlaget","ut fra","tilleggsdekningen"},
  {"Uten hensyn til","en tilpasningsdyktig","skjønnsvurdering","utløses","bonusbestemmelsene","med sikte på","bransjestandarden"},
  {"Avhengig av","en sesongvariert","skadetakst","stabiliseres","hovedforfallet","på tvers av","løsningssporet"},
  {"På grunnlag av","en gjennomsnittlig","bygningsdel","påvirkes","rammebetingelsene","på linje med","tariffparametrene"},
  {"I og med","en skademessig","forsikringstaker","avtales","unntaksreglene","utenom","markedsandelene"},
  {"Under henvisning til","en trendsettende","purrerutine","synliggjøres","kompensasjonen","i forlengelsen av","konsekvensaspektet"},
  {"Under henvisning til","en effektbasert","avdelingskultur","realiseres","sluttilstanden","utenom","operasjonen"},
  {"På grunnlag av","en operativ","doktrine","omsettes","hensikten","i forlengelsen av","kommandonivået"},
  {"Med bakrunn i","en fellesoperativ","kommando","implementeres","virkefeltet","innenfor","kampanjeplanen"},
  {"Gitt","en militærstrategisk","operasjonsdimensjon","reflekteres","samvirket","i utnyttelsen av","profesjonen"},
  {"Avhengig av","en taktisk","manøver","utvikles","kommandokjeden","ut fra","forsvarsgrenen"},
  {"Sett i lys av","en stridsteknisk","samhandling","kjennetegnes","avskrekkingseffekten","ut over","våpengrenen"},
  {"Uten hensyn til","en dynamisk","konflikt","sammenfattes","synergieffekten","på tvers av","alliansen"},
  {"I og med","en konvensjonell","krigføring","iverksettes","friksjonen","i tilknytning til","kjerneverdiene"},
  {"Etter en totalvurdering av","en ukonvensjonell","maktbruk","avgjøres","planverket","i forbindelse med","konseptutviklingen"},
  {"Under analysen av","en fredsbevarende","styrkemultiplikator","tilpasses","styrkebidraget","med hensyn til ","militærmakten"},
  {"I kontrollen av","en tverrsektoriell","interoperabilitet","operasjonaliseres","virkemiddelet","gjennom","krigens natur"},
  {"I arbeidet med","en overordnet","konfliktintensitet","påvirkes","lendevurderingen","med basis i ","handlingsrommet"},
  {"Under forutsetning av","en nødvendig","konflikthåndtering","identifiseres","fiendevurderingen","til støtte for","deployeringen"},
  {"Sammenholdt med","en offensiv","avskrekking","organiseres","stridsfeltet","ved bruk av","operasjonsområdet"},
  {"Sett hen til","en defensiv","myndighetsutøvelse","fastsettes","evnen","på linje med","styrkedisponeringen"},
  {"Etter etablering av","en desentralisert","prosessavhengighet","styrkes","stridsevnen","parallellt med","langtidsplanen"},
  {"Grunnet","en oppdragsbasert","beredskap","koordineres","innsatsmidlet","i relasjon til","sikkerhetspolitikken"},
  {"Vedrørende","en maritim","kraftsamling","bevisstgjøres","situasjonsforståelsen","i forhold til ","oppdragsorganiseringen"},
  {"Forutsatt","en spesialisert","ressursbruk","forsterkes","schwerpunktet","i motsetning til","totalforsvaret"},
  {"Etter planlegging av","en indirekte","beslutningsevne","fokuseres","trusselbildet","i tillegg til","feltfunksjonen"},
  {"Ved bruk av","en sentralisert","handlekraft","samordnes","uttaket","innenfor rammen av","brigadestrukturen"},
  {"Gitt","en forskningsbasert","målsetting","synliggjøres","forebyggingspotensialet","innenfor rammen av","sykehussammenslåingen"},
  {"Under hensyntagen til","en optimal","detaljering","overstyres","helserisikoen","som en følge av","DRG-produksjonen"},
  {"I lys av","en sømløs","struktur","iverksettes","helseministerens standpunkt","for så vidt gjelder","rehabiliteringsprosjekt"},
  {"Vedrørende","en pasientorientert","agenda","identifiseres","samhandlingsarenaene","med henblikk på","styringsmodellen"},
  {"Grunnet","en proaktiv","tidshorisont","initieres","brukerrepresentasjonen","i forhold til","leveransekonsept"},
  {"I betraktning av","en betydelig","optimalisering","lokaliseres","kunnskapsforankringen","hva angår","IA-arbeidet"},
  {"Forutsatt","en tverrfaglig","mobilitet","kommuniseres","statsekretærens vurdering","parallelt med","nasjonale helsemål"},
  {"Med utgangspunkt i","en vesentlig","treffsikkerhet","styrkes","HR-direktørens beslutning","i relasjon til","turnusforskriften"},
  {"I forhold til","en helsepolitisk","innsats","realiseres","brukertilfredsheten","i tilknytning til","utfordringsrommet"},
  {"Sett hen til","en kostnadseffektiv","kvalitetssikring","effektueres","helseinformasjonsflyten","på bakgrunn av","helsevisjonene"},
  {"I henhold til","en avtagende","problematikk","forankres","samhandlingspotensialet","avhengig av","rekrutteringsutfordringene"},
  {"Med tanke på","en vedvarende","ressursbruk","maksimeres","økonomisjefens målhorisont","hva gjelder","feilbehandlingen"},
  {"Uavhengig av","en tiltagende","omstilling","konkretiseres","sykehusdirektørens visjon","koordinert med","tilsynsrapporten"},
  {"Sett på bakgrunn av","en helsefaglig","avklaring","tilgjengeliggjøres","helsegevinsten","på samme måte som","omstillingsprosessen"},
  {"Sammenholdt med","en helhetlig","implementering","utvides","helseutfordringene","i motsetning til","leveransekonseptet"},
  {"På grunn av","en manglende","styringsinnsats","dokumenteres","helseproduksjonen","innenfor","innsatsfokuseringen"},
  {"Med hensyn til","en særlig","innovasjon","spores","fastlegenes produksjonsevne","i tillegg til","helseforetaket"},
  {"Under forutsetning av","en løpende","forventningsjustering","innhentes","rehabiliteringspotensialet","gjennom","ventelisteskandalene"},
  {"Etter en totalvurdering av","en langsiktig","orientering","adresseres","samhandlingskompetansen","ut fra","tjenesteintegreringen"},
  {"Uten hensyn til","en bærekraftig","utvikling","stabiliseres","forsvarlighetskravene","med sikte på","kjernevirksomheten"},
  {"Avhengig av","en resultatorientert","målhorisont","genereres","samhandlingskoordineringen","på tvers av","pasientsskadeerstatningen"},
  {"På grunnlag av","en tverrfaglig","arena","stimuleres","avviksmeldingene","på linje med","kvalitetsarbeidet"},
  {"I og med","en kunnskapsbasert","synergi","balanseres","fagkoordineringen","utenom","systemsvikten"},
  {"Under henvisning til","en inkluderende","avstemning","ivaretas","helseproblematikken","i forlengelsen av","helsereformene"},
  {"Gitt","en representativ","beskrivelse","dokumenteres","visualiseringen","innenfor rammen av","kommunepolitikken"},
  {"I lys av","en implisitt","gjengivelse","ivaretas","byggesøknaden","i motsetning til","samordningsplikten"},
  {"Med bakgrunn i","en konkret","søknad","utredes","materialiteten","i tillegg til","saksbehandlingstiden"},
  {"I forhold til","en feilaktig","skjemautfylling","fastslås","beskaffenheten","gjennom","tiltakshaveren"},
  {"Sett hen til","en faktisk","råderett","meldes","kontrollerklæringen","parallelt med","dispensasjonssøknaden"},
  {"I og med","en nødvendig","nabovarsling","konkluderes","søknadsprosessen","for så vidt gjelder","reguleringsplanen"},
  {"Ifølge","en transparent","klagebehandling","betviles","vedtaksbegrunnelsen","på basis av","kartgrunnlaget"},
  {"Sammenliknet med","en langsiktig","saksbehandling","oppleves","forføyningen","innenfor","hjemmelsgrunnlaget"},
  {"Ut fra","en bærekraftig","samhandling","betraktes","gjennomføringsplanen","i forhold til","planvedtaket"},
  {"Uavhengig av","en representativ","prosessavklaring","estimeres","ansvarsbelegningen","med henblikk på","ansvarsretten"},
  {"Basert på","en kvalitativ","konkretisering","bedømmes","tiltaket","ved hjelp av","prosjekteringen"},
  {"Vedrørende","en søknadspliktig","innvilgelse","anbefales","igangsettingstillatelsen","på linje med","planforslaget"},
  {"Under henvisning til","en klanderverdig","kvalitetsbedømming","forhåndsvurderes","ferdigattesten","på tvers av","grunnboken"},
  {"Grunnet","en arkitekturbasert","ulempebetraktning","avklares","kvaliteten","knyttet til","planbestemmelsen"},
  {"Tatt i betraktning","en systematisk","tillatelse","preaksepteres","dispensasjonen","som en følge av","arealbruken"},
  {"Forutsatt","(en ressursmessig)","realitetsbehandling","fastsettes","samsvarserklæringen","begrunnet i","statlig arealplan"},
  {"Uten hensyn til","en vesentlig","arealoverføring","forankres","oppføringen","i kraft av","tomtefestelova"},
  {"Med sikte på","en midlertidig","godkjennelse","utløses","rammetillatelsen","på tross av","strandsonen"},
  {"Sett ut fra","en kvalifisert","omregulering","samordnes","fasadeendringen","i relasjon til","ansvarsretten"},
  {"Med grunnlag i","en frittliggende","rammetillatelse","vurderes","terrenginngrepet","i overensstemmelse med","tvingende sikkerhetshensyn"},
}

PRIVMSG:
  '^%psvada$': (s, d, a) =>
    words = {}
    wordN = 1
    for i=1,7 do
      line = svada[math.random(1, #svada)]
      word = line[wordN]
      table.insert(words, word)
      wordN = wordN + 1

    say table.concat(words, ' ')

error: pathspec 'modules/svada.moon' did not match any file(s) known to git


mannen: super silly module to check some one time event

Former-commit-id: 581a7967644f2cad02ce5d9980f435183606839a [formerly 150634658507eb02e0429906b16b277f4c2c2bec]
Former-commit-id: 6fd02e7f7a434ed20c986c8f7c44d1f44903582f

PRIVMSG:
  '^%pmannen$': =>
    ivar2.util.simplehttp 'http://www.vondess.com/mannen/api', (data) ->
      res = ivar2.util.json.decode data
      if res.falt_ned
        say 'Mannen har falt ned. Fucking finally.'
      else unless res.falt_ned
        say 'Nei. Mannen har ikke falt ned.'

error: pathspec 'modules/mannen.moon' did not match any file(s) known to git


Incomplete tests added for Structure.

Incomplete tests added for Structure.

describe "Structure", ->
	Structure = require "game.structure"
	StructureReference = require "game.structure_reference"

	TEST_REFERENCE = StructureReference
		name: "Test Structure"

	it "Structure {}", ->
		Structure
			reference: TEST_REFERENCE

	it "Structure {} -- errors", ->
		f = ->
			Structure {}

		assert.has_error f, "no 'reference' attribute provided"

error: pathspec 'spec/structure.moon' did not match any file(s) known to git


Add applyLines script.

Add applyLines script.

-- This script takes one line and turns it into many lines.

-- Usage: select 1 line (or a bunch, it actually doesn't matter because it only
-- uses the active line). Run the macro. It will copy all of the shape data from
-- AI2ASS on the clipboard into Aegisub, creating as many lines as necessary.
-- Note that due to me being a fucking idiot, it will just kind of paste
-- whatever the hell you have on the clipboard into the current line. I don't
-- care to fix this. I doubt anyone except me will ever use this. If you do and
-- you don't like this behavior, pull requests welcome.

require "clipboard"

string.split = ( sep ) =>
	sep, fields = sep or ":", {}
	string.gsub @, "([^#{sep}]+)", (c) -> table.insert fields, c
	fields

-- Creates lines on a glyph by glyph basis because libass eats shit on very long
-- lines. This looks terrible on fades and ends up bloating the script a lot
-- more, but it's easier than actually fixing libass myself. Oh, and all the
-- colors are hardcoded because I don't give half a shit. Fix it yourself if you
-- want to use it.
applyInnerShadow = ( sub, originalLine, lineTable ) ->
	len = #lineTable
	for x = 2, len-2, 2
		error "DONGS" if aegisub.progress.is_cancelled!
		originalLine.text = [[{\pos(2,2)\c&H000000&\blur2}]] .. lineTable[x]
		originalLine.layer = 2
		sub.insert originalLine.n, originalLine
		originalLine.text = [[{\pos(0,0)\c&H0000FF&}]] .. lineTable[x-1]
		originalLine.layer = 1
		sub.insert originalLine.n, originalLine
		originalLine.text = [[{\3c&HFFFFFF&\bord8}]] .. originalLine.text
		originalLine.layer = 0
		sub.insert originalLine.n, originalLine

	originalLine.text = [[{\pos(2,2)\c&H000000&\blur2}]] .. lineTable[len]
	originalLine.layer = 2
	sub.insert originalLine.n, originalLine
	originalLine.text = [[{\pos(0,0)\c&H0000FF&}]] .. lineTable[len-1]
	originalLine.layer = 1
	sub.insert originalLine.n, originalLine
	originalLine.text = [[{\3c&HFFFFFF&\bord8}]] .. originalLine.text
	originalLine.layer = 0
	sub[originalLine.n-1] = originalLine

createInnerShadow = ( sub, sel, act ) ->
	originalLine = sub[act]
	originalLine.n = act + 1
	incomingLines = clipboard.get!
	lineTable = incomingLines\split "\r\n"
	if lineTable[1]\match "{innerShadow}"
		table.remove lineTable, 1
		if #lineTable % 2 == 0
			applyInnerShadow sub, originalLine, lineTable
		else
			aegisub.log 0, "This shit is malformed and everything you do is wrong."
	elseif lineTable[1]\match "{allLayers}"
		table.remove lineTable, 1
		applyAllLayers sub, originalLine, lineTable
	else
		originalLine.text = lineTable
		sub[act] = originalLine

aegisub.register_macro "Apply AI Lines", "Handles paste data from AI2ASS", createInnerShadow

error: pathspec 'applyLines.moon' did not match any file(s) known to git


beginning of a window element

beginning of a window element

--- A generic window element. Supports resizing, minimizing(?), and closing.
--- @classmod window
--- @copyright Paul Liverman III (2016)
--- @license The MIT License (MIT)

class window
    --- Constructor expects nothing, or a data table describing it.
    new: (@parent, @data={}) =>
        --- @todo if data, do stuff about it

    setSize: =>
        --do more stuff!

error: pathspec 'elements/window.moon' did not match any file(s) known to git