Update de.moon

Update de.moon

-- Copyright (C) 2017-2019 DBot

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.

-- editor stuffs

gui.ppm2.dxlevel.not_supported = 'Dein DirectX™ Level ist zu niedrig. Mindestens ist 9.0 erforderlich. Wenn Du 8.1 für die Bildrate verwendest,\nDann hast Du entweder eine alte Grafikkarte oder schlechte Treiber.\nDenn die Bildrate in gmod kann nur niedrig sein, weil andere Addons nutzlos hohe CPU-Last erzeugen.\nJa! Diese Meldung wird mehrmals erscheinen, um Dich zu ärgern. Denn, WARUM HAST DU DICH DAN ÜBER FEHLENDE TEXTUREN GEMELDET????'
gui.ppm2.dxlevel.toolow = 'DirectX™ level ist zu niedrig für PPM/2'

gui.ppm2.editor.eyes.separate = 'Getrennte Einstellungen für Augen verwenden'
gui.ppm2.editor.eyes.url = 'Augen URL Textur'
gui.ppm2.editor.eyes.url_desc = 'Bei Verwendung der Augen URL Textur; Die folgenden Optionen haben keine Auswirkung'

gui.ppm2.editor.eyes.lightwarp_desc = 'Lightwarp wirkt nur auf EyeRefract Augen'
gui.ppm2.editor.eyes.lightwarp = "Lightwarp"
gui.ppm2.editor.eyes.desc1 = "Lightwarp textur URL eingabe\nEs muss 256x16 sein!"
gui.ppm2.editor.eyes.desc2 = "Glanzstärke\nDieser Parameter erhöht die Stärke der Echtzeitreflexionen auf das Auge\nUm Änderungen zu sehen, setze ppm2_cl_reflections convar auf 1\nAndere Spieler würden Reflexionen nur sehen, wenn ppm2_cl_reflections auf 1 gesetzt ist\n0 - ist matt; 1 - ist spiegelnd"

for _, {tprefix, prefix} in ipairs {{'def', ''}, {'left', 'Links '}, {'right', 'Rechts '}}
	gui.ppm2.editor.eyes[tprefix].lightwarp.shader = "#{prefix}EyeRefract Shader verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.cornera = "#{prefix}Eye Cornea diffus verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.glossiness = "#{prefix}Glanz"

	gui.ppm2.editor.eyes[tprefix].type = "#{prefix}Augentyp"
	gui.ppm2.editor.eyes[tprefix].reflection_type = "#{prefix}Augenreflexion typ"
	gui.ppm2.editor.eyes[tprefix].lines = "#{prefix}Augenlinien"
	gui.ppm2.editor.eyes[tprefix].derp = "#{prefix}Derp Augen"
	gui.ppm2.editor.eyes[tprefix].derp_strength = "#{prefix}Derp Augen Stärke"
	gui.ppm2.editor.eyes[tprefix].iris_size = "#{prefix}Augengröße"

	gui.ppm2.editor.eyes[tprefix].points_inside = "#{prefix}Augenlinien innen"
	gui.ppm2.editor.eyes[tprefix].width = "#{prefix}Augenbreite"
	gui.ppm2.editor.eyes[tprefix].height = "#{prefix}Augenhöhe"

	gui.ppm2.editor.eyes[tprefix].pupil.width = "#{prefix}Pupillenbreite"
	gui.ppm2.editor.eyes[tprefix].pupil.height = "#{prefix}Pupillenhöhe"
	gui.ppm2.editor.eyes[tprefix].pupil.size = "#{prefix}Pupillengröße"

	gui.ppm2.editor.eyes[tprefix].pupil.shift_x = "#{prefix}Pupillen Verschiebung X"
	gui.ppm2.editor.eyes[tprefix].pupil.shift_y = "#{prefix}Pupillen Verschiebung Y"
	gui.ppm2.editor.eyes[tprefix].pupil.rotation = "#{prefix}Augenrotation"

	gui.ppm2.editor.eyes[tprefix].background = "#{prefix}Augenhintergrund"
	gui.ppm2.editor.eyes[tprefix].pupil_size = "#{prefix}Pupille"
	gui.ppm2.editor.eyes[tprefix].top_iris = "#{prefix}Obere Augeniris"
	gui.ppm2.editor.eyes[tprefix].bottom_iris = "#{prefix}Untere Augeniris"
	gui.ppm2.editor.eyes[tprefix].line1 = "#{prefix}Augenlinie 1"
	gui.ppm2.editor.eyes[tprefix].line2 = "#{prefix}Augenlinie 2"
	gui.ppm2.editor.eyes[tprefix].reflection = "#{prefix}Augenreflexionseffekt"
	gui.ppm2.editor.eyes[tprefix].effect = "#{prefix}Augeneffekt"

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 (ungespeicherte Änderungen!)'

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\nDatenwert: %q'
gui.ppm2.editor.generic.url = '%s\n\nLink geht zu: %s'
gui.ppm2.editor.generic.url_field = 'URL Feld'
gui.ppm2.editor.generic.spoiler = 'Geheimnisvoller Spoiler'

gui.ppm2.editor.generic.restart.needed = 'Neustart des Editors erforderlich'
gui.ppm2.editor.generic.restart.text = 'Du solltest den Editor neu starten, um die Änderung zu übernehmen.\nJetzt neu starten?\nNicht gespeicherte Daten gehen verloren!'
gui.ppm2.editor.generic.fullbright = 'Volle Helligkeit'
gui.ppm2.editor.generic.wtf = 'Aus irgendeinem Grund hat Dein Player kein NetworkedPonyData. - Nichts zu bearbeiten!\nVersuche ppm2_reload in Deine Konsole einzugeben und versuche es erneut, den Editor zu öffnen.'

gui.ppm2.editor.io.random = 'Randomisieren!'
gui.ppm2.editor.io.newfile.title = 'Neue Datei'
gui.ppm2.editor.io.newfile.confirm = 'Willst Du wirklich eine neue Datei erstellen?'
gui.ppm2.editor.io.newfile.toptext = 'Zurücksetzen'
gui.ppm2.editor.io.delete.confirm = 'Willst Du diese Datei wirklich löschen?\nEs wird für immer weg sein!\n(eine lange Zeit!)'
gui.ppm2.editor.io.delete.title = 'Wirklich löschen?'

gui.ppm2.editor.io.filename = 'Dateiname'
gui.ppm2.editor.io.hint = 'Datei per Doppelklick öffnen'
gui.ppm2.editor.io.reload = 'Dateiliste neu laden'
gui.ppm2.editor.io.failed = 'Import fehlgeschlagen.'

gui.ppm2.editor.io.warn.oldfile = '!!! Es kann funktionieren oder auch nicht. Du wirst zerquetscht werden.'
gui.ppm2.editor.io.warn.text = "Derzeit hast du deine Änderungen nicht angegeben.\nWillst Du wirklich eine andere Datei öffnen?"
gui.ppm2.editor.io.warn.header = 'Nicht gespeicherte Änderungen!'
gui.ppm2.editor.io.save.button = 'Speichern'
gui.ppm2.editor.io.save.text = 'Dateiname ohne ppm2/ und .dat eingeben\nTipp: Um als Autoload zu speichern, schreibe "_current" (ohne ") ein.'
gui.ppm2.editor.io.wear = 'übernehmen (Anziehen)'

gui.ppm2.editor.seq.standing = 'Stehen'
gui.ppm2.editor.seq.move = 'Bewegen'
gui.ppm2.editor.seq.walk = 'Gehen'
gui.ppm2.editor.seq.sit = 'Sitzen'
gui.ppm2.editor.seq.swim = 'Schwimmen'
gui.ppm2.editor.seq.run = 'Laufen'
gui.ppm2.editor.seq.duckwalk = 'Ducken gehen'
gui.ppm2.editor.seq.duck = 'Ducken'
gui.ppm2.editor.seq.jump = 'Springen'

gui.ppm2.editor.misc.race = 'Rasse'
gui.ppm2.editor.misc.weight = 'Gewicht'
gui.ppm2.editor.misc.size = 'Pony Größe'
gui.ppm2.editor.misc.hide_weapons = 'Waffen verstecken'
gui.ppm2.editor.misc.chest = 'Männliche Brust'
gui.ppm2.editor.misc.gender = 'Geschlecht'
gui.ppm2.editor.misc.wings = 'Flügel Typ'
gui.ppm2.editor.misc.flexes = 'Flex steuerung'
gui.ppm2.editor.misc.no_flexes2 = 'Kein Flex beim neuen Modell'
gui.ppm2.editor.misc.no_flexes_desc = 'Du kannst jeden Flex Zustandsregler separat deaktivieren.\nSo können diese Flexe mit Addons von Drittanbietern (wie PAC3) modifiziert werden.'

gui.ppm2.editor.misc.hide_pac3 = 'Entitys ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_mane = 'Mähne ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_tail = 'Schweif ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_socks = 'Socken ausblenden wenn PAC3 Entity verwendet wird'

gui.ppm2.editor.tabs.main = 'Allgemeines'
gui.ppm2.editor.tabs.files = 'Dateien'
gui.ppm2.editor.tabs.old_files = 'Alte Dateien'
gui.ppm2.editor.tabs.cutiemark = 'Schönheitsfleck'
gui.ppm2.editor.tabs.head = 'Kopfanatomie'
gui.ppm2.editor.tabs.eyes = 'Augen'
gui.ppm2.editor.tabs.face = 'Gesicht'
gui.ppm2.editor.tabs.mouth = 'Mund'
gui.ppm2.editor.tabs.left_eye = 'Linkes Auge'
gui.ppm2.editor.tabs.right_eye = 'Rechtes Auge'
gui.ppm2.editor.tabs.mane_horn = 'Mähne und Horn'
gui.ppm2.editor.tabs.mane = 'Mähne'
gui.ppm2.editor.tabs.details = 'Details'
gui.ppm2.editor.tabs.url_details = 'URL Details'
gui.ppm2.editor.tabs.url_separated_details = 'URL Getrennte Details'
gui.ppm2.editor.tabs.ears = 'Ohren'
gui.ppm2.editor.tabs.horn = 'Horn'
gui.ppm2.editor.tabs.back = 'Rücken'
gui.ppm2.editor.tabs.wings = 'Flügel'
gui.ppm2.editor.tabs.left = 'Links'
gui.ppm2.editor.tabs.right = 'Rechts'
gui.ppm2.editor.tabs.neck = 'Hals'
gui.ppm2.editor.tabs.body = 'Pony Körper'
gui.ppm2.editor.tabs.tattoos = 'Tätowierungen'
gui.ppm2.editor.tabs.tail = 'Schweif'
gui.ppm2.editor.tabs.hooves = 'Huf Anatomie'
gui.ppm2.editor.tabs.bottom_hoof = 'Huf unten'
gui.ppm2.editor.tabs.legs = 'Beine'
gui.ppm2.editor.tabs.socks = 'Socken'
gui.ppm2.editor.tabs.newsocks = 'Neue Socken'
gui.ppm2.editor.tabs.about = 'Über'

gui.ppm2.editor.old_tabs.mane_tail = 'Mähne und Schweif'
gui.ppm2.editor.old_tabs.wings_and_horn_details = 'Flügel und Horn Details'
gui.ppm2.editor.old_tabs.wings_and_horn = 'Flügel und Horn'
gui.ppm2.editor.old_tabs.body_details = 'Körper Details'
gui.ppm2.editor.old_tabs.mane_tail_detals = 'Mähne und Schweif URL Details'

gui.ppm2.editor.cutiemark.display = 'Schönheitsfleck anzeigen'
gui.ppm2.editor.cutiemark.type = 'Schönheitsfleck typ'
gui.ppm2.editor.cutiemark.size = 'Schönheitsfleck größe'
gui.ppm2.editor.cutiemark.color = 'Schönheitsfleck Farbe'
gui.ppm2.editor.cutiemark.input = 'Schönheitsfleck URL bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL texture)'

gui.ppm2.editor.face.eyelashes = 'Wimpern'
gui.ppm2.editor.face.eyelashes_color = 'Wimpern Farbe'
gui.ppm2.editor.face.eyelashes_phong = 'Wimpern Phong Parameter'
gui.ppm2.editor.face.eyebrows_color = 'Augenbrauen Farbe'
gui.ppm2.editor.face.new_muzzle = 'Neue Schnauze für das männliche Modell nutzen'

gui.ppm2.editor.face.nose = 'Nasenfarbe'
gui.ppm2.editor.face.lips = 'Lippenfarbe'
gui.ppm2.editor.face.eyelashes_separate_phong = 'Separate Wimpern Phong'
gui.ppm2.editor.face.eyebrows_glow = 'leuchtende Augenbrauen'
gui.ppm2.editor.face.eyebrows_glow_strength = 'Augenbrauen Leuchtstärke'
gui.ppm2.editor.face.inherit.lips = 'Erbe Lippen Farbe vom Körper'
gui.ppm2.editor.face.inherit.nose = 'Erbe Nasen Farbe vom Körper'

gui.ppm2.editor.mouth.fangs = 'Fangzähne'
gui.ppm2.editor.mouth.alt_fangs = 'Alternative Fangzähne'
gui.ppm2.editor.mouth.claw = 'Klauenzähne'

gui.ppm2.editor.mouth.teeth = 'Zahnfarbe'
gui.ppm2.editor.mouth.teeth_phong = 'Zahn Phong Parameter'
gui.ppm2.editor.mouth.mouth = 'Mundfarbe'
gui.ppm2.editor.mouth.mouth_phong = 'Mund Phong Parameter'
gui.ppm2.editor.mouth.tongue = 'Zungenfarbe'
gui.ppm2.editor.mouth.tongue_phong = 'Zunge Phong Parameter'

gui.ppm2.editor.mane.type = 'Mähne Typ'
gui.ppm2.editor.mane.phong = 'Trenne Mähne phong Einstellungen vom Körper'
gui.ppm2.editor.mane.mane_phong = 'Mähne Phong Parameter'
gui.ppm2.editor.mane.phong_sep = 'Trenne obere und untere Mähnenfarbe'
gui.ppm2.editor.mane.up.phong = 'Obere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.down.type = 'Untere Mähne Typ'
gui.ppm2.editor.mane.down.phong = 'Untere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.newnotice = 'Die nächsten Optionen wirken sich nur auf das neue Modell aus'

for i = 1, 2
	gui.ppm2.editor.mane['color' .. i] = "Mähnefarbe #{i}"
	gui.ppm2.editor.mane.up['color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['color' .. i] = "Untere Mähnefarbe #{i}"

for i = 1, 6
	gui.ppm2.editor.mane['detail_color' .. i] = "Mähne Detail Farbe #{i}"
	gui.ppm2.editor.mane.up['detail_color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['detail_color' .. i] = "Untere Mähnefarbe #{i}"

	gui.ppm2.editor.url_mane['desc' .. i] = "Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane['color' .. i] = "Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_tail['desc' .. i] = "Schweif URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_tail['color' .. i] = "Schweif URL detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.up['desc' .. i] = "Obere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.up['color' .. i] = "Obere Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.down['desc' .. i] = "Untere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.down['color' .. i] = "Untere Mähne URL Detail Farbe #{i}"

gui.ppm2.editor.ears.bat = 'Bat pony Ohren'
gui.ppm2.editor.ears.size = 'Ohrengröße'

gui.ppm2.editor.horn.detail_color = 'Horn Detailfarbe'
gui.ppm2.editor.horn.glowing_detail = 'Horn leuchten Detail'
gui.ppm2.editor.horn.glow_strength = 'Horn leuchten Stärke'
gui.ppm2.editor.horn.separate_color = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.color = 'Horn Farbe'
gui.ppm2.editor.horn.horn_phong = 'Horn Phong Parameter'
gui.ppm2.editor.horn.magic = 'Horn magie Farbe'
gui.ppm2.editor.horn.separate_magic_color = 'Trenne magie Farbe vom Augen Farbe'
gui.ppm2.editor.horn.separate = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.separate_phong = 'Trenne Horn phong Einstellungen vom Körper'

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

gui.ppm2.editor.wings.separate_color = 'Trenne Flügel Farbe vom Körper'
gui.ppm2.editor.wings.color = 'Flügelfarbe'
gui.ppm2.editor.wings.wings_phong = 'Flügel Phong Parameter'
gui.ppm2.editor.wings.separate = 'Trenne Flügelfarbe vom Körper'
gui.ppm2.editor.wings.separate_phong = 'Trenne Flügel phong Einstellungen vom Körper'
gui.ppm2.editor.wings.bat_color = 'Bat Flügelfarbe'
gui.ppm2.editor.wings.bat_skin_color = 'Bat Flügel Hautfarbe'
gui.ppm2.editor.wings.bat_skin_phong = 'Bat Flügel Haut Phong Parameter'

gui.ppm2.editor.wings.normal = 'Normale Flügel'
gui.ppm2.editor.wings.bat = 'Bat Flügel'
gui.ppm2.editor.wings.bat_skin = 'Bat Flügel Haut'

gui.ppm2.editor.wings.left.size = 'Linker Flügel Größe'
gui.ppm2.editor.wings.left.fwd = 'Linker Flügel Vorwärts'
gui.ppm2.editor.wings.left.up = 'Linker Flügel Hoch'
gui.ppm2.editor.wings.left.inside = 'Linker Flügel Innen'

gui.ppm2.editor.wings.right.size = 'Rechter Flügel Größe'
gui.ppm2.editor.wings.right.fwd = 'Rechter Flügel Vorwärts'
gui.ppm2.editor.wings.right.up = 'Rechter Flügel Hoch'
gui.ppm2.editor.wings.right.inside = 'Rechter Flügel Innen'

for i = 1, 3
	gui.ppm2.editor.wings.details.def['detail' .. i] = "Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.def['color' .. i] = "URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.bat['detail' .. i] = "Bat Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.bat['color' .. i] = "Bat Flügel URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.batskin['detail' .. i] = "Bat Flügel Haut URL Detail #{i}"
	gui.ppm2.editor.wings.details.batskin['color' .. i] = "Bat Flügel Haut URL Detail Farbe #{i}"

gui.ppm2.editor.neck.height = 'Halshöhe'

gui.ppm2.editor.body.suit = 'Körperanzug'
gui.ppm2.editor.body.color = 'Körperfarbe'
gui.ppm2.editor.body.body_phong = 'Körper Phong Parameter'
gui.ppm2.editor.body.spine_length = 'Rückenlänge'
gui.ppm2.editor.body.url_desc = 'Körper Detail URL Bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL textur)'

gui.ppm2.editor.body.disable_hoofsteps = 'Hufschritte Deaktivieren'
gui.ppm2.editor.body.disable_wander_sounds = 'Wandergeräusche Deaktivieren'
gui.ppm2.editor.body.disable_new_step_sounds = 'Neue Laufgeräusche Deaktivieren'
gui.ppm2.editor.body.disable_jump_sound = 'Sprung geräusche Deaktivieren'
gui.ppm2.editor.body.disable_falldown_sound = 'Sprung geräusche Deaktivieren'
gui.ppm2.editor.body.call_playerfootstep = 'PlayerFootstep bei jedem Sound aufrufen'
gui.ppm2.editor.body.call_playerfootstep_desc = 'Ruft den PlayerFootstep hook bei jedem tatsächlichen Sound auf, den Du hörst.\nDurch die Verwendung dieser Option können sich andere installierte Addons auf die Immersion von PPM2 verlassen,\ndie den PlayerFootstep hook hören. Dies sollte nur deaktiviert werden, wenn es zu unzuverlässige Ergebnisse von anderen Addons kommt\noder Deine FPS auf niedrige Werte fallen, da eines der installierten Addons schlecht Programmiert wurde.'

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

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

gui.ppm2.editor.tattoo.edit_keyboard = 'Bearbeiten mit der Tastatur'
gui.ppm2.editor.tattoo.type = 'Typ'
gui.ppm2.editor.tattoo.over = 'Tattoo über Körperdetails'
gui.ppm2.editor.tattoo.glow = 'Tattoo leuchtet'
gui.ppm2.editor.tattoo.glow_strength = 'Tattoo leuchtkraft'
gui.ppm2.editor.tattoo.color = 'Farbe der Tätowierung'

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 = 'Breite Skalierung'
gui.ppm2.editor.tattoo.tweak.height = 'Länge Skalierung'

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

gui.ppm2.editor.tail.type = 'Schweif typ'
gui.ppm2.editor.tail.size = 'Schweif größe'
gui.ppm2.editor.tail.tail_phong = 'Schweif Phong Parameter'
gui.ppm2.editor.tail.separate = 'Trenne Schweif phong Einstellungen vom Körper'

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

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

gui.ppm2.editor.hoof.fluffers = 'Huf flausch'

gui.ppm2.editor.legs.height = 'Bein höhe'
gui.ppm2.editor.legs.socks.simple = 'Socken (einfache Textur)'
gui.ppm2.editor.legs.socks.model = 'Socken (als Modell)'
gui.ppm2.editor.legs.socks.color = 'Socken Modell Farbe'
gui.ppm2.editor.legs.socks.socks_phong = 'Socken Phong Parameter'
gui.ppm2.editor.legs.socks.texture = 'Socken Textur'
gui.ppm2.editor.legs.socks.url_texture = 'Socken URL textur'

for i = 1, 6
	gui.ppm2.editor.legs.socks['color' .. i] = 'Socken Detail Farbe ' .. i

gui.ppm2.editor.legs.newsocks.model = 'Socken (als neues Modell)'

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

gui.ppm2.editor.legs.newsocks.url = 'Neue Socken URL textur'

-- shared editor stuffs

gui.ppm2.editor.tattoo.help = "Um den Bearbeitungsmodus zu verlassen, drücke Escape oder klicke irgendwo mit der Maus
Um das Tattoo zu bewegen, verwende WASD
Um höher/niedriger zu skalieren, verwende die Hoch/Runter Pfeiltasten
Um breiter/kleiner zu skalieren, verwende die Rechts/Links Pfeiltasten
Um links/rechts zu Drehen, verwende die Q/E Tasten"

gui.ppm2.editor.reset_value = 'Zurücksetzen %s'

gui.ppm2.editor.phong.info = 'Mehr Infos über Phong im Wiki'
gui.ppm2.editor.phong.exponent = 'Phong Exponent - wie stark die reflektierende Eigenschaft\nvon der Ponyhaut ist\nSetze den Wert nahe Null, um dem Roboterlook zu bekommen\nPonyhaut'
gui.ppm2.editor.phong.exponent_text = 'Phong Exponent'
gui.ppm2.editor.phong.boost.title = 'Phong Verstärkung - multipliziert spiegelnde Kartenreflexionen'
gui.ppm2.editor.phong.boost.boost = 'Phong Verstärkung'
gui.ppm2.editor.phong.tint.title = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.tint.tint = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.frensel.front.title = 'Phong Front - Fresnel 0 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.front.front = 'Phong Front'
gui.ppm2.editor.phong.frensel.middle.title = 'Phong Mitte - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.middle.front = 'Phong Mitte'
gui.ppm2.editor.phong.frensel.sliding.title = 'Phong Gleitend - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.sliding.front = 'Phong Gleitend'
gui.ppm2.editor.phong.lightwarp = 'Lightwarp'
gui.ppm2.editor.phong.url_lightwarp = 'Lightwarp textur URL eingabe\nEs muss 256x16 sein!'
gui.ppm2.editor.phong.bumpmap = 'Bumpmap URL eingabe'

gui.ppm2.editor.info.discord = "Trete DBot's Discord bei!"
gui.ppm2.editor.info.ponyscape = "PPM/2 ist ein Ponyscape projekt"
gui.ppm2.editor.info.creator = "PPM/2 wurde von DBot erstellt und entwickelt"
gui.ppm2.editor.info.newmodels = "Neue Modelle wurden von Durpy erstellt"
gui.ppm2.editor.info.cppmmodels = "CPPM Modelle (einschließlich Ponyhände) gehören zu UnkN', 'http://steamcommunity.com/profiles/76561198084938735"
gui.ppm2.editor.info.oldmodels = "Alte Modelle gehören zu Scentus und den anderen."
gui.ppm2.editor.info.bugs = "Einen Fehler gefunden? Melde es hier!"
gui.ppm2.editor.info.sources = "Quellen findest du hier"
gui.ppm2.editor.info.githubsources = "Oder im GitHub mirror"
gui.ppm2.editor.info.thanks = "Besonderer Dank geht an alle, die kritisiert,\ngeholfen und PPM/2 getestet haben!"

-- other stuff

info.ppm2.fly.pegasus = 'Du musst eine Pegasus oder ein Alicorn sein, um zu fliegen!'
info.ppm2.fly.cannot = 'Du kannst nicht %s im Augenblick.'

gui.ppm2.emotes.sad = 'Traurig'
gui.ppm2.emotes.wild = 'Wild'
gui.ppm2.emotes.grin = 'Grinsen'
gui.ppm2.emotes.angry = 'Wütend'
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 = 'Lippen lecken'
gui.ppm2.emotes.scrunch = 'Scrunch'
gui.ppm2.emotes.sorry = 'Entschuldigend'
gui.ppm2.emotes.wink = 'Zwinkern'
gui.ppm2.emotes.right_wink = 'Rechts Zwinkern'
gui.ppm2.emotes.licking = 'Lecken'
gui.ppm2.emotes.suggestive_lips = 'Anzüglich Lippen Lecken'
gui.ppm2.emotes.suggestive_no_tongue = 'Anzüglich ohne Zunge'
gui.ppm2.emotes.gulp = 'Schlucken'
gui.ppm2.emotes.blah = 'Blah blah blah'
gui.ppm2.emotes.happi = 'Glücklich'
gui.ppm2.emotes.happi_grin = 'Glückliches Grinsen'
gui.ppm2.emotes.duck = 'ENTE'
gui.ppm2.emotes.ducks = 'ENTEN WAHNSINN'
gui.ppm2.emotes.quack = 'QUACK'
gui.ppm2.emotes.suggestive = 'Anzüglich mit Zunge'

message.ppm2.emotes.invalid = 'Keine Emotion mit ID: %s'

gui.ppm2.editor.intro.text = "Grüße meinen neuen.... Robochirurgen für Ponys! Es erlaubt Dir....\n" ..
	"hmmm... ein Pony zu werden, und ja, dieser Prozess ist UNUMKEHRBAR! Aber mach dir keine Sorgen,\n" ..
	"Du verlierst keine deiner Gehirnzellen, in und nach der Operation, weil wir die Operation sehr schonend durchführen...\n\n" ..
	"Eigentlich habe ich keine Ahnung, du biologisches Wesen! Seine mechanischen Hände werden dich in die engsten Umarmungen hüllen, die du je bekommen hast!\n" ..
	"Und, bitte, sterbe nicht in diesen Prozess, denn dies würde dazu führen DAS ERLÖSCHEN DEINER LEBENSLANGEN GARANTIE... und Du wirst kein Pony sein!\n" ..
	"----\n\n\n" ..
	"Vorsicht: Nehme den Robochirurgen nicht auseinander.\nLege deine Hände/Hufe nicht in bewegliche Teile des Robochirurgen.\n" ..
	"Schalte es während des Betriebs nicht aus..\nVersuche nicht sich seinen Taten zu widersetzen.\n" ..
	"Sei immer sanft mit den Robochirurgen.\n" ..
	"Schlage den Robochirurgen nie auf sein Interface.\n" ..
	"DBot's DLibCo übernimmt keine Verantwortung für Schäden, die durch falsche Verwendung von Robochirurgen entstehen.\n" ..
	"Die Garantie erlischt, wenn der Benutzer stirbt.\n" ..
	"Keine Rückerstattung."
gui.ppm2.editor.intro.title = 'Willkommen hier, Mensch!'
gui.ppm2.editor.intro.okay = "Ok, ich werde diese Lizenz sowieso nie lesen."

-- Copyright (C) 2017-2019 DBot

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-- of the Software, and to permit persons to whom the Software is furnished to do so,
-- subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all copies
-- or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-- DEALINGS IN THE SOFTWARE.

-- editor stuffs

gui.ppm2.editor.eyes.separate = 'Getrennte Einstellungen für Augen verwenden'
gui.ppm2.editor.eyes.url = 'Augen URL Textur'
gui.ppm2.editor.eyes.url_desc = 'Bei Verwendung der Augen URL Textur; Die folgenden Optionen haben keine Auswirkung'

gui.ppm2.editor.eyes.lightwarp_desc = 'Lightwarp wirkt nur auf EyeRefract Augen'
gui.ppm2.editor.eyes.lightwarp = "Lightwarp"
gui.ppm2.editor.eyes.desc1 = "Lightwarp textur URL eingabe\nEs muss 256x16 sein!"
gui.ppm2.editor.eyes.desc2 = "Glanzstärke\nDieser Parameter erhöht die Stärke der Echtzeitreflexionen auf das Auge\nUm Änderungen zu sehen, setze ppm2_cl_reflections convar auf 1\nAndere Spieler würden Reflexionen nur sehen, wenn ppm2_cl_reflections auf 1 gesetzt ist\n0 - ist matt; 1 - ist spiegelnd"

for _, {tprefix, prefix} in ipairs {{'def', ''}, {'left', 'Links '}, {'right', 'Rechts '}}
	gui.ppm2.editor.eyes[tprefix].lightwarp.shader = "#{prefix}EyeRefract Shader verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.cornera = "#{prefix}Eye Cornea diffus verwenden"
	gui.ppm2.editor.eyes[tprefix].lightwarp.glossiness = "#{prefix}Glanz"

	gui.ppm2.editor.eyes[tprefix].type = "#{prefix}Augentyp"
	gui.ppm2.editor.eyes[tprefix].reflection_type = "#{prefix}Augenreflexion typ"
	gui.ppm2.editor.eyes[tprefix].lines = "#{prefix}Augenlinien"
	gui.ppm2.editor.eyes[tprefix].derp = "#{prefix}Derp Augen"
	gui.ppm2.editor.eyes[tprefix].derp_strength = "#{prefix}Derp Augen Stärke"
	gui.ppm2.editor.eyes[tprefix].iris_size = "#{prefix}Augengröße"

	gui.ppm2.editor.eyes[tprefix].points_inside = "#{prefix}Augenlinien innen"
	gui.ppm2.editor.eyes[tprefix].width = "#{prefix}Augenbreite"
	gui.ppm2.editor.eyes[tprefix].height = "#{prefix}Augenhöhe"

	gui.ppm2.editor.eyes[tprefix].pupil.width = "#{prefix}Pupillenbreite"
	gui.ppm2.editor.eyes[tprefix].pupil.height = "#{prefix}Pupillenhöhe"
	gui.ppm2.editor.eyes[tprefix].pupil.size = "#{prefix}Pupillengröße"

	gui.ppm2.editor.eyes[tprefix].pupil.shift_x = "#{prefix}Pupillen Verschiebung X"
	gui.ppm2.editor.eyes[tprefix].pupil.shift_y = "#{prefix}Pupillen Verschiebung Y"
	gui.ppm2.editor.eyes[tprefix].pupil.rotation = "#{prefix}Augenrotation"

	gui.ppm2.editor.eyes[tprefix].background = "#{prefix}Augenhintergrund"
	gui.ppm2.editor.eyes[tprefix].pupil_size = "#{prefix}Pupille"
	gui.ppm2.editor.eyes[tprefix].top_iris = "#{prefix}Obere Augeniris"
	gui.ppm2.editor.eyes[tprefix].bottom_iris = "#{prefix}Untere Augeniris"
	gui.ppm2.editor.eyes[tprefix].line1 = "#{prefix}Augenlinie 1"
	gui.ppm2.editor.eyes[tprefix].line2 = "#{prefix}Augenlinie 2"
	gui.ppm2.editor.eyes[tprefix].reflection = "#{prefix}Augenreflexionseffekt"
	gui.ppm2.editor.eyes[tprefix].effect = "#{prefix}Augeneffekt"

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 (ungespeicherte Änderungen!)'

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\nDatenwert: %q'
gui.ppm2.editor.generic.url = '%s\n\nLink geht zu: %s'
gui.ppm2.editor.generic.url_field = 'URL Feld'
gui.ppm2.editor.generic.spoiler = 'Geheimnisvoller Spoiler'

gui.ppm2.editor.generic.restart.needed = 'Neustart des Editors erforderlich'
gui.ppm2.editor.generic.restart.text = 'Du solltest den Editor neu starten, um die Änderung zu übernehmen.\nJetzt neu starten?\nNicht gespeicherte Daten gehen verloren!'
gui.ppm2.editor.generic.fullbright = 'Volle Helligkeit'
gui.ppm2.editor.generic.wtf = 'Aus irgendeinem Grund hat Dein Player kein NetworkedPonyData. - Nichts zu bearbeiten!\nVersuche ppm2_reload in Deine Konsole einzugeben und versuche es erneut, den Editor zu öffnen.'

gui.ppm2.editor.io.random = 'Randomisieren!'
gui.ppm2.editor.io.newfile.title = 'Neue Datei'
gui.ppm2.editor.io.newfile.confirm = 'Willst Du wirklich eine neue Datei erstellen?'
gui.ppm2.editor.io.newfile.toptext = 'Zurücksetzen'
gui.ppm2.editor.io.delete.confirm = 'Willst Du diese Datei wirklich löschen?\nEs wird für immer weg sein!\n(eine lange Zeit!)'
gui.ppm2.editor.io.delete.title = 'Wirklich löschen?'

gui.ppm2.editor.io.filename = 'Dateiname'
gui.ppm2.editor.io.hint = 'Datei per Doppelklick öffnen'
gui.ppm2.editor.io.reload = 'Dateiliste neu laden'
gui.ppm2.editor.io.failed = 'Import fehlgeschlagen.'

gui.ppm2.editor.io.warn.oldfile = '!!! Es kann funktionieren oder auch nicht. Du wirst zerquetscht werden.'
gui.ppm2.editor.io.warn.text = "Derzeit hast du deine Änderungen nicht angegeben.\nWillst Du wirklich eine andere Datei öffnen?"
gui.ppm2.editor.io.warn.header = 'Nicht gespeicherte Änderungen!'
gui.ppm2.editor.io.save.button = 'Speichern'
gui.ppm2.editor.io.save.text = 'Dateiname ohne ppm2/ und .dat eingeben\nTipp: Um als Autoload zu speichern, schreibe "_current" (ohne ") ein.'
gui.ppm2.editor.io.wear = 'übernehmen (Anziehen)'

gui.ppm2.editor.seq.standing = 'Stehen'
gui.ppm2.editor.seq.move = 'Bewegen'
gui.ppm2.editor.seq.walk = 'Gehen'
gui.ppm2.editor.seq.sit = 'Sitzen'
gui.ppm2.editor.seq.swim = 'Schwimmen'
gui.ppm2.editor.seq.run = 'Laufen'
gui.ppm2.editor.seq.duckwalk = 'Ducken gehen'
gui.ppm2.editor.seq.duck = 'Ducken'
gui.ppm2.editor.seq.jump = 'Springen'

gui.ppm2.editor.misc.race = 'Rasse'
gui.ppm2.editor.misc.weight = 'Gewicht'
gui.ppm2.editor.misc.size = 'Pony Größe'
gui.ppm2.editor.misc.hide_weapons = 'Waffen verstecken'
gui.ppm2.editor.misc.chest = 'Männliche Brust'
gui.ppm2.editor.misc.gender = 'Geschlecht'
gui.ppm2.editor.misc.wings = 'Flügel Typ'
gui.ppm2.editor.misc.flexes = 'Flex steuerung'
gui.ppm2.editor.misc.no_flexes2 = 'Kein Flex beim neuen Modell'
gui.ppm2.editor.misc.no_flexes_desc = 'Du kannst jeden Flex Zustandsregler separat deaktivieren.\nSo können diese Flexe mit Addons von Drittanbietern (wie PAC3) modifiziert werden.'

gui.ppm2.editor.misc.hide_pac3 = 'Entitys ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_mane = 'Mähne ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_tail = 'Schweif ausblenden wenn PAC3 Entity verwendet wird'
gui.ppm2.editor.misc.hide_socks = 'Socken ausblenden wenn PAC3 Entity verwendet wird'

gui.ppm2.editor.tabs.main = 'Allgemeines'
gui.ppm2.editor.tabs.files = 'Dateien'
gui.ppm2.editor.tabs.old_files = 'Alte Dateien'
gui.ppm2.editor.tabs.cutiemark = 'Schönheitsfleck'
gui.ppm2.editor.tabs.head = 'Kopfanatomie'
gui.ppm2.editor.tabs.eyes = 'Augen'
gui.ppm2.editor.tabs.face = 'Gesicht'
gui.ppm2.editor.tabs.mouth = 'Mund'
gui.ppm2.editor.tabs.left_eye = 'Linkes Auge'
gui.ppm2.editor.tabs.right_eye = 'Rechtes Auge'
gui.ppm2.editor.tabs.mane_horn = 'Mähne und Horn'
gui.ppm2.editor.tabs.mane = 'Mähne'
gui.ppm2.editor.tabs.details = 'Details'
gui.ppm2.editor.tabs.url_details = 'URL Details'
gui.ppm2.editor.tabs.url_separated_details = 'URL Getrennte Details'
gui.ppm2.editor.tabs.ears = 'Ohren'
gui.ppm2.editor.tabs.horn = 'Horn'
gui.ppm2.editor.tabs.back = 'Rücken'
gui.ppm2.editor.tabs.wings = 'Flügel'
gui.ppm2.editor.tabs.left = 'Links'
gui.ppm2.editor.tabs.right = 'Rechts'
gui.ppm2.editor.tabs.neck = 'Hals'
gui.ppm2.editor.tabs.body = 'Pony Körper'
gui.ppm2.editor.tabs.tattoos = 'Tätowierungen'
gui.ppm2.editor.tabs.tail = 'Schweif'
gui.ppm2.editor.tabs.hooves = 'Huf Anatomie'
gui.ppm2.editor.tabs.bottom_hoof = 'Huf unten'
gui.ppm2.editor.tabs.legs = 'Beine'
gui.ppm2.editor.tabs.socks = 'Socken'
gui.ppm2.editor.tabs.newsocks = 'Neue Socken'
gui.ppm2.editor.tabs.about = 'Über'

gui.ppm2.editor.old_tabs.mane_tail = 'Mähne und Schweif'
gui.ppm2.editor.old_tabs.wings_and_horn_details = 'Flügel und Horn Details'
gui.ppm2.editor.old_tabs.wings_and_horn = 'Flügel und Horn'
gui.ppm2.editor.old_tabs.body_details = 'Körper Details'
gui.ppm2.editor.old_tabs.mane_tail_detals = 'Mähne und Schweif URL Details'

gui.ppm2.editor.cutiemark.display = 'Schönheitsfleck anzeigen'
gui.ppm2.editor.cutiemark.type = 'Schönheitsfleck typ'
gui.ppm2.editor.cutiemark.size = 'Schönheitsfleck größe'
gui.ppm2.editor.cutiemark.color = 'Schönheitsfleck Farbe'
gui.ppm2.editor.cutiemark.input = 'Schönheitsfleck URL bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL texture)'

gui.ppm2.editor.face.eyelashes = 'Wimpern'
gui.ppm2.editor.face.eyelashes_color = 'Wimpern Farbe'
gui.ppm2.editor.face.eyelashes_phong = 'Wimpern Phong Parameter'
gui.ppm2.editor.face.eyebrows_color = 'Augenbrauen Farbe'
gui.ppm2.editor.face.new_muzzle = 'Neue Schnauze für das männliche Modell nutzen'

gui.ppm2.editor.face.nose = 'Nasenfarbe'
gui.ppm2.editor.face.lips = 'Lippenfarbe'
gui.ppm2.editor.face.eyelashes_separate_phong = 'Separate Wimpern Phong'
gui.ppm2.editor.face.eyebrows_glow = 'leuchtende Augenbrauen'
gui.ppm2.editor.face.eyebrows_glow_strength = 'Augenbrauen Leuchtstärke'
gui.ppm2.editor.face.inherit.lips = 'Erbe Lippen Farbe vom Körper'
gui.ppm2.editor.face.inherit.nose = 'Erbe Nasen Farbe vom Körper'

gui.ppm2.editor.mouth.fangs = 'Fangzähne'
gui.ppm2.editor.mouth.alt_fangs = 'Alternative Fangzähne'
gui.ppm2.editor.mouth.claw = 'Klauenzähne'

gui.ppm2.editor.mouth.teeth = 'Zahnfarbe'
gui.ppm2.editor.mouth.teeth_phong = 'Zahn Phong Parameter'
gui.ppm2.editor.mouth.mouth = 'Mundfarbe'
gui.ppm2.editor.mouth.mouth_phong = 'Mund Phong Parameter'
gui.ppm2.editor.mouth.tongue = 'Zungenfarbe'
gui.ppm2.editor.mouth.tongue_phong = 'Zunge Phong Parameter'

gui.ppm2.editor.mane.type = 'Mähne Typ'
gui.ppm2.editor.mane.phong = 'Trenne Mähne phong Einstellungen vom Körper'
gui.ppm2.editor.mane.mane_phong = 'Mähne Phong Parameter'
gui.ppm2.editor.mane.phong_sep = 'Trenne obere und untere Mähnenfarbe'
gui.ppm2.editor.mane.up.phong = 'Obere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.down.type = 'Untere Mähne Typ'
gui.ppm2.editor.mane.down.phong = 'Untere Mähne Phong Einstellungen'
gui.ppm2.editor.mane.newnotice = 'Die nächsten Optionen wirken sich nur auf das neue Modell aus'

for i = 1, 2
	gui.ppm2.editor.mane['color' .. i] = "Mähnefarbe #{i}"
	gui.ppm2.editor.mane.up['color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['color' .. i] = "Untere Mähnefarbe #{i}"

for i = 1, 6
	gui.ppm2.editor.mane['detail_color' .. i] = "Mähne Detail Farbe #{i}"
	gui.ppm2.editor.mane.up['detail_color' .. i] = "Obere Mähnefarbe #{i}"
	gui.ppm2.editor.mane.down['detail_color' .. i] = "Untere Mähnefarbe #{i}"

	gui.ppm2.editor.url_mane['desc' .. i] = "Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane['color' .. i] = "Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.up['desc' .. i] = "Obere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.up['color' .. i] = "Obere Mähne URL Detail Farbe #{i}"

	gui.ppm2.editor.url_mane.sep.down['desc' .. i] = "Untere Mähne URL Detail #{i} Eingabefeld"
	gui.ppm2.editor.url_mane.sep.down['color' .. i] = "Untere Mähne URL Detail Farbe #{i}"

gui.ppm2.editor.ears.bat = 'Bat pony Ohren'
gui.ppm2.editor.ears.size = 'Ohrengröße'

gui.ppm2.editor.horn.detail_color = 'Horn Detailfarbe'
gui.ppm2.editor.horn.glowing_detail = 'Horn leuchten Detail'
gui.ppm2.editor.horn.glow_strength = 'Horn leuchten Stärke'
gui.ppm2.editor.horn.separate_color = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.color = 'Horn Farbe'
gui.ppm2.editor.horn.horn_phong = 'Horn Phong Parameter'
gui.ppm2.editor.horn.magic = 'Horn magie Farbe'
gui.ppm2.editor.horn.separate_magic_color = 'Trenne magie Farbe vom Augen Farbe'
gui.ppm2.editor.horn.separate = 'Trenne Horn Farbe vom Körper'
gui.ppm2.editor.horn.separate_phong = 'Trenne Horn phong Einstellungen vom Körper'

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

gui.ppm2.editor.wings.separate_color = 'Trenne Flügel Farbe vom Körper'
gui.ppm2.editor.wings.color = 'Flügelfarbe'
gui.ppm2.editor.wings.wings_phong = 'Flügel Phong Parameter'
gui.ppm2.editor.wings.separate = 'Trenne Flügelfarbe vom Körper'
gui.ppm2.editor.wings.separate_phong = 'Trenne Flügel phong Einstellungen vom Körper'
gui.ppm2.editor.wings.bat_color = 'Bat Flügelfarbe'
gui.ppm2.editor.wings.bat_skin_color = 'Bat Flügel Hautfarbe'
gui.ppm2.editor.wings.bat_skin_phong = 'Bat Flügel Haut Phong Parameter'

gui.ppm2.editor.wings.normal = 'Normale Flügel'
gui.ppm2.editor.wings.bat = 'Bat Flügel'
gui.ppm2.editor.wings.bat_skin = 'Bat Flügel Haut'

gui.ppm2.editor.wings.left.size = 'Linker Flügel Größe'
gui.ppm2.editor.wings.left.fwd = 'Linker Flügel Vorwärts'
gui.ppm2.editor.wings.left.up = 'Linker Flügel Hoch'
gui.ppm2.editor.wings.left.inside = 'Linker Flügel Innen'

gui.ppm2.editor.wings.right.size = 'Rechter Flügel Größe'
gui.ppm2.editor.wings.right.fwd = 'Rechter Flügel Vorwärts'
gui.ppm2.editor.wings.right.up = 'Rechter Flügel Hoch'
gui.ppm2.editor.wings.right.inside = 'Rechter Flügel Innen'

for i = 1, 3
	gui.ppm2.editor.wings.details.def['detail' .. i] = "Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.def['color' .. i] = "URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.bat['detail' .. i] = "Bat Flügel URL Detail #{i}"
	gui.ppm2.editor.wings.details.bat['color' .. i] = "Bat Flügel URL Detail Farbe #{i}"
	gui.ppm2.editor.wings.details.batskin['detail' .. i] = "Bat Flügel Haut URL Detail #{i}"
	gui.ppm2.editor.wings.details.batskin['color' .. i] = "Bat Flügel Haut URL Detail Farbe #{i}"

gui.ppm2.editor.neck.height = 'Halshöhe'

gui.ppm2.editor.body.suit = 'Körperanzug'
gui.ppm2.editor.body.color = 'Körperfarbe'
gui.ppm2.editor.body.body_phong = 'Körper Phong Parameter'
gui.ppm2.editor.body.spine_length = 'Rückenlänge'
gui.ppm2.editor.body.url_desc = 'Körper Detail URL Bild Eingabefeld\nSollte PNG oder JPEG sein (funktioniert wie bei\nPAC3 URL textur)'

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

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

gui.ppm2.editor.tattoo.edit_keyboard = 'Bearbeiten mit der Tastatur'
gui.ppm2.editor.tattoo.type = 'Typ'
gui.ppm2.editor.tattoo.over = 'Tattoo über Körperdetails'
gui.ppm2.editor.tattoo.glow = 'Tattoo leuchtet'
gui.ppm2.editor.tattoo.glow_strength = 'Tattoo leuchtkraft'
gui.ppm2.editor.tattoo.color = 'Farbe der Tätowierung'

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 = 'Breite Skalierung'
gui.ppm2.editor.tattoo.tweak.height = 'Länge Skalierung'

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

gui.ppm2.editor.tail.type = 'Schweif typ'
gui.ppm2.editor.tail.size = 'Schweif größe'
gui.ppm2.editor.tail.tail_phong = 'Schweif Phong Parameter'
gui.ppm2.editor.tail.separate = 'Trenne Schweif phong Einstellungen vom Körper'

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

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

gui.ppm2.editor.hoof.fluffers = 'Huf flausch'

gui.ppm2.editor.legs.height = 'Bein höhe'
gui.ppm2.editor.legs.socks.simple = 'Socken (einfache Textur)'
gui.ppm2.editor.legs.socks.model = 'Socken (als Modell)'
gui.ppm2.editor.legs.socks.color = 'Socken Modell Farbe'
gui.ppm2.editor.legs.socks.socks_phong = 'Socken Phong Parameter'
gui.ppm2.editor.legs.socks.texture = 'Socken Textur'
gui.ppm2.editor.legs.socks.url_texture = 'Socken URL textur'

for i = 1, 6
	gui.ppm2.editor.legs.socks['color' .. i] = 'Socken Detail Farbe ' .. i

gui.ppm2.editor.legs.newsocks.model = 'Socken (als neues Modell)'

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

gui.ppm2.editor.legs.newsocks.url = 'Neue Socken URL textur'

-- shared editor stuffs

gui.ppm2.editor.tattoo.help = "Um den Bearbeitungsmodus zu verlassen, drücke Escape oder klicke irgendwo mit der Maus
Um das Tattoo zu bewegen, verwende WASD
Um höher/niedriger zu skalieren, verwende die Hoch/Runter Pfeiltasten
Um breiter/kleiner zu skalieren, verwende die Rechts/Links Pfeiltasten
Um links/rechts zu Drehen, verwende die Q/E Tasten"

gui.ppm2.editor.reset_value = 'Zurücksetzen %s'

gui.ppm2.editor.phong.info = 'Mehr Infos über Phong im Wiki'
gui.ppm2.editor.phong.exponent = 'Phong Exponent - wie stark die reflektierende Eigenschaft\nvon der Ponyhaut ist\nSetze den Wert nahe Null, um dem Roboterlook zu bekommen\nPonyhaut'
gui.ppm2.editor.phong.exponent_text = 'Phong Exponent'
gui.ppm2.editor.phong.boost.title = 'Phong Verstärkung - multipliziert spiegelnde Kartenreflexionen'
gui.ppm2.editor.phong.boost.boost = 'Phong Verstärkung'
gui.ppm2.editor.phong.tint.title = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.tint.tint = 'Tönung Farbe - welche Farben reflektieren Spiegelkarte\nWeiß - Reflektiert alle Farben\n(Im weißen Raum - weiße Spiegelkarte)'
gui.ppm2.editor.phong.frensel.front.title = 'Phong Front - Fresnel 0 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.front.front = 'Phong Front'
gui.ppm2.editor.phong.frensel.middle.title = 'Phong Mitte - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.middle.front = 'Phong Mitte'
gui.ppm2.editor.phong.frensel.sliding.title = 'Phong Gleitend - Fresnel 45 Grad Reflexionswinkel Multiplikator'
gui.ppm2.editor.phong.frensel.sliding.front = 'Phong Gleitend'
gui.ppm2.editor.phong.lightwarp = 'Lightwarp'
gui.ppm2.editor.phong.url_lightwarp = 'Lightwarp textur URL eingabe\nEs muss 256x16 sein!'
gui.ppm2.editor.phong.bumpmap = 'Bumpmap URL eingabe'

gui.ppm2.editor.info.discord = "Trete DBot's Discord bei!"
gui.ppm2.editor.info.ponyscape = "PPM/2 ist ein Ponyscape projekt"
gui.ppm2.editor.info.creator = "PPM/2 wurde von DBot erstellt und entwickelt"
gui.ppm2.editor.info.newmodels = "Neue Modelle wurden von Durpy erstellt"
gui.ppm2.editor.info.cppmmodels = "CPPM Modelle (einschließlich Ponyhände) gehören zu UnkN', 'http://steamcommunity.com/profiles/76561198084938735"
gui.ppm2.editor.info.oldmodels = "Alte Modelle gehören zu Scentus und den anderen."
gui.ppm2.editor.info.bugs = "Einen Fehler gefunden? Melde es hier!"
gui.ppm2.editor.info.sources = "Quellen findest du hier"
gui.ppm2.editor.info.githubsources = "Oder im GitHub mirror"
gui.ppm2.editor.info.thanks = "Besonderer Dank geht an alle, die kritisiert,\ngeholfen und PPM/2 getestet haben!"

-- other stuff

info.ppm2.fly.pegasus = 'Du musst eine Pegasus oder ein Alicorn sein, um zu fliegen!'
info.ppm2.fly.cannot = 'Du kannst nicht %s im Augenblick.'

gui.ppm2.emotes.sad = 'Traurig'
gui.ppm2.emotes.wild = 'Wild'
gui.ppm2.emotes.grin = 'Grinsen'
gui.ppm2.emotes.angry = 'Wütend'
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 = 'Lippen lecken'
gui.ppm2.emotes.scrunch = 'Scrunch'
gui.ppm2.emotes.sorry = 'Entschuldigend'
gui.ppm2.emotes.wink = 'Zwinkern'
gui.ppm2.emotes.right_wink = 'Rechts Zwinkern'
gui.ppm2.emotes.licking = 'Lecken'
gui.ppm2.emotes.suggestive_lips = 'Anzüglich Lippen Lecken'
gui.ppm2.emotes.suggestive_no_tongue = 'Anzüglich ohne Zunge'
gui.ppm2.emotes.gulp = 'Schlucken'
gui.ppm2.emotes.blah = 'Blah blah blah'
gui.ppm2.emotes.happi = 'Glücklich'
gui.ppm2.emotes.happi_grin = 'Glückliches Grinsen'
gui.ppm2.emotes.duck = 'ENTE'
gui.ppm2.emotes.ducks = 'ENTEN WAHNSINN'
gui.ppm2.emotes.quack = 'QUACK'
gui.ppm2.emotes.suggestive = 'Anzüglich mit Zunge'

message.ppm2.emotes.invalid = 'Keine Emotion mit ID: %s'

gui.ppm2.editor.intro.text = "Grüße meinen neuen.... Robochirurgen für Ponys! Es erlaubt Dir....\n" ..
	"hmmm... ein Pony zu werden, und ja, dieser Prozess ist UNUMKEHRBAR! Aber mach dir keine Sorgen,\n" ..
	"Du verlierst keine deiner Gehirnzellen, in und nach der Operation, weil wir die Operation sehr schonend durchführen...\n\n" ..
	"Eigentlich habe ich keine Ahnung, du biologisches Wesen! Seine mechanischen Hände werden dich in die engsten Umarmungen hüllen, die du je bekommen hast!\n" ..
	"Und, bitte, sterbe nicht in diesen Prozess, denn dies würde dazu führen DAS ERLÖSCHEN DEINER LEBENSLANGEN GARANTIE... und Du wirst kein Pony sein!\n" ..
	"----\n\n\n" ..
	"Vorsicht: Nehme den Robochirurgen nicht auseinander.\nLege deine Hände/Hufe nicht in bewegliche Teile des Robochirurgen.\n" ..
	"Schalte es während des Betriebs nicht aus..\nVersuche nicht sich seinen Taten zu widersetzen.\n" ..
	"Sei immer sanft mit den Robochirurgen.\n" ..
	"Schlage den Robochirurgen nie auf sein Interface.\n" ..
	"DBot's DLibCo übernimmt keine Verantwortung für Schäden, die durch falsche Verwendung von Robochirurgen entstehen.\n" ..
	"Die Garantie erlischt, wenn der Benutzer stirbt.\n" ..
	"Keine Rückerstattung."
gui.ppm2.editor.intro.title = 'Willkommen hier, Mensch!'
gui.ppm2.editor.intro.okay = "Ok, ich werde diese Lizenz sowieso nie lesen."


github: some color to increase readability

util = require 'util'

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

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

acolor = (action) ->
  if action == 'opened'
    return util.green action
  if action == 'closed'
    return util.red action
  if action == 'deleted'
    return util.red action
  if action == 'synchronize'
    return util.purple action

  return action

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

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

  repo = json.repository.full_name

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

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

return {}

util = require 'util'

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

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

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

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

  repo = json.repository.full_name

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

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

return {}


github: improve push formatting

github: improve push formatting

util = require 'util'
hmac = require "openssl.hmac" -- from luaossl

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

tohex = (b) ->
  x = ""
  for i = 1, #b do
    x = x .. string.format("%.2x", string.byte(b, i))
  return 'sha1='..x

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

acolor = (action) ->
  if action == 'opened'
    return util.green action
  if action == 'closed'
    return util.red action
  if action == 'deleted'
    return util.red action
  if action == 'synchronize'
    return util.purple action
  if action == 'labeled'
    return util.yellow action

  return action

strip_email = (text) ->
  out = {}
  for line in text\gmatch'[^\r\n]+'
    unless line\match'^%s*>'
      out[#out+1] = line
  table.concat out

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    out = {"[#{repo}]: #{branch}"}
    lastauthor = ''
    for i, c in ipairs json.commits
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      if i == 1
        message ..= ' ' .. json.compare
      author = ''
      if c.author.username ~= lastauthor
        author = "<#{util.nonickalertword c.author.username}>"
      out[#out+1] = "#{author} #{message}\n"
      lastauthor = c.author.username
    if #json.commits >= 4
      out[#out+1] = "#{#json.commits-3} more commits not displayed."
    table.concat out, ' '
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 #{util.yellow '★'} (Total: #{json.repository.stargazers_count})"
    else
      "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    "[#{repo}]: <#{util.nonickalertword json.sender.login}> #{body}"
  pull_request: (repo, destination, json) ->
    action = acolor json.action
    number = util.bold json.number
    "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  pull_request_review_comment: (repo, destination, json) ->
    body = json.comment.body
    number = util.bold json.pull_request.number
    "[#{repo}]: PR ##{number} <#{util.nonickalertword json.sender.login}> #{body}"
  issues: (repo, destination, json) ->
    action = acolor json.action
    nr = util.bold json.issue.number
    extra = ''
    if json.action == 'labeled' -- Add label and color if action is label
      extra = "#{json.label.name}, ##{json.label.color} "
    "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{extra}#{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = acolor json.action
    nr = util.bold json.issue.number
    "[#{repo}]: Issue ##{nr} <#{util.nonickalertword json.sender.login}> #{strip_email json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
  status: (repo, destination, json) -> -- Travis CI event
    -- NYI
    return

}

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

  repo = json.repository.full_name

  secret = req.headers['x-hub-signature']
  if secret
    confsecret = ivar2.config.channels[destination].githubSecret
    if not confsecret
      ivar2\Log('error', 'github event, no secret configured for destination: %s', destination)
      return
    mysum = tohex(hmac.new(confsecret)\final(req.body))
    if tostring(mysum) != tostring(secret)
      ivar2\Log('error', 'github event with invalid signature for destination: %s', destination)
      res\append ':status', "403"
      res\append 'Content-Type', 'text/plain'
      req\write_headers(res, false, 30)
      req\write_body_from_string('invalid secret', 30)
      return

  event = req.headers['x-github-event']
  handler = handlers[event]
  if handler
    if message = handler(repo, destination, json)
      ivar2\Msg 'privmsg', destination, nil, message
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\append ':status', "200"
  res\append 'Content-Type', 'text/plain'
  req\write_headers(res, false, 30)
  req\write_body_from_string('ok', 30)

util = require 'util'
hmac = require "openssl.hmac" -- from luaossl

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

tohex = (b) ->
  x = ""
  for i = 1, #b do
    x = x .. string.format("%.2x", string.byte(b, i))
  return 'sha1='..x

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

acolor = (action) ->
  if action == 'opened'
    return util.green action
  if action == 'closed'
    return util.red action
  if action == 'deleted'
    return util.red action
  if action == 'synchronize'
    return util.purple action
  if action == 'labeled'
    return util.yellow action

  return action

strip_email = (text) ->
  out = {}
  for line in text\gmatch'[^\r\n]+'
    unless line\match'^%s*>'
      out[#out+1] = line
  table.concat out

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

}

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

  repo = json.repository.full_name

  secret = req.headers['x-hub-signature']
  if secret
    confsecret = ivar2.config.channels[destination].githubSecret
    if not confsecret
      ivar2\Log('error', 'github event, no secret configured for destination: %s', destination)
      return
    mysum = tohex(hmac.new(confsecret)\final(req.body))
    if tostring(mysum) != tostring(secret)
      ivar2\Log('error', 'github event with invalid signature for destination: %s', destination)
      res\append ':status', "403"
      res\append 'Content-Type', 'text/plain'
      req\write_headers(res, false, 30)
      req\write_body_from_string('invalid secret', 30)
      return

  event = req.headers['x-github-event']
  handler = handlers[event]
  if handler
    if message = handler(repo, destination, json)
      ivar2\Msg 'privmsg', destination, nil, message
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\append ':status', "200"
  res\append 'Content-Type', 'text/plain'
  req\write_headers(res, false, 30)
  req\write_body_from_string('ok', 30)


github: no highlights

github: no highlights

Former-commit-id: 0ea478a6bf111ab6f108ba0c4f289602381c8d0d [formerly 43b8d35eddc72651bf9f883347e436f299816317]
Former-commit-id: a90f8b0e105107bb83d5bf37f7ee9d71dc204553

util = require 'util'

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

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

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

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

  repo = json.repository.full_name

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

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

return {}

util = require 'util'

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

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

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

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

  repo = json.repository.full_name

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

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

return {}


logger: log the bot's own messages

logger: log the bot's own messages

Former-commit-id: 8f9fec5a1ca591015b2fa05b91116aa75276080c [formerly a2e788e9063d10780b5f3137baac9bc026b9e820]
Former-commit-id: 0fe4736e7fd7de4b9fb0a8fcd956dedfaab37d50

dbi = require 'DBI'
require'logging.console'
iconv = require'iconv'
iso2utf = iconv.new('UTF-8'.."//TRANSLIT", 'ISO-8859-1')
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);]]
}

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

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 = ->
    ins = dbh!\prepare('INSERT INTO log(nick,channel,message,type) values(?,?,?,?)')
    ins\execute(nick, destination, arg, type)

  -- First try to insert statement directly
  -- if that doesn't work; convert it from iso to utf and try again
  stmt, err = insert!
  unless stmt
    arg, err = toutf(arg)
    stmt, err = insert!
    unless stmt
      log\error err

history = (source, destination, nr) ->
  nr = tonumber(nr) or 1
  -- TODO ignore messages that are commands
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      ORDER BY time
      DESC LIMIT ?
    ]]
  stmt\execute destination, nr
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

lastlog = (source, destination, arg) ->
  nr = tonumber(nr) or 1
  arg = '%'..arg..'%'
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      AND message LIKE ?
      ORDER BY time DESC
      LIMIT 20
    ]]
  stmt\execute destination, arg
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

seen = (source, destination, nick) =>
  nick = ivar2.util.trim(nick)
  stmt, err = dbh!\prepare [[
    SELECT
        *,
        date_trunc('second', time) as sectime,
        date_trunc('second', age(now(), date_trunc('second', time))) as ago
      FROM log
      WHERE nick = ?
      ORDER BY time DESC
      LIMIT 1
    ]]
  unless stmt
    print(err)
  stmt\execute nick
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  actions =
    'ACTION': 'talking in third person'
    'TOPIC': 'changing topic'
    'PRIVMSG': 'jabbering'
    'NICK': 'changing nick'
    'QUIT': 'quitting'
    'PART': 'leaving'
    'NOTICE': 'sending a notice'
    'MODE': 'changing a mode'
    'JOIN': 'joining'

  if #out == 1
    r = out[1]
    message = ''
    if #r.message
      message == " with message #{r.message}"
    say "#{ivar2.util.bold r.nick} was last observed #{ivar2.util.italic actions[r.type]}#{message} #{r.ago} ago (#{r.sectime} UTC)"
  else
    say "#{ivar2.util.bold nick} was not seen in my lifetime."

return {
  PRIVMSG: {
    '^%pseen (.+)$': seen
    '^%plast$': (source, destination) =>
      say history(source,destination,1)
    '^%plastlog (.+)$': (source, destination, arg) =>
      out = {}
      for k,v in *lastlog(source, destination, arg)
        -- Skip commands
        unless k.message\match('^%p')
          table.insert out, string.format('<%s> %s', k.nick, k.message)
      say table.concat(out, ' ')
    (s, d, a) =>
      dblog @, 'PRIVMSG', s, d, a
  }
  PRIVMSG_OUT: {
    (s, d, a) =>
      dblog @, 'PRIVMSG', s, d, a
  }
  NOTICE_OUT: {
    (s, d, a) =>
      dblog @, 'NOTICE', 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
  }
}

dbi = require 'DBI'
require'logging.console'
iconv = require'iconv'
iso2utf = iconv.new('UTF-8'.."//TRANSLIT", 'ISO-8859-1')
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);]]
}

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

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 = ->
    ins = dbh!\prepare('INSERT INTO log(nick,channel,message,type) values(?,?,?,?)')
    ins\execute(nick, destination, arg, type)

  -- First try to insert statement directly
  -- if that doesn't work; convert it from iso to utf and try again
  stmt, err = insert!
  unless stmt
    arg, err = toutf(arg)
    stmt, err = insert!
    unless stmt
      log\error err

history = (source, destination, nr) ->
  nr = tonumber(nr) or 1
  -- TODO ignore messages that are commands
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      ORDER BY time
      DESC LIMIT ?
    ]]
  stmt\execute destination, nr
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

lastlog = (source, destination, arg) ->
  nr = tonumber(nr) or 1
  arg = '%'..arg..'%'
  stmt = dbh!\prepare [[
    SELECT
      *
      FROM log
      WHERE channel=?
      AND (type = 'PRIVMSG' OR type = 'ACTION')
      AND message LIKE ?
      ORDER BY time DESC
      LIMIT 20
    ]]
  stmt\execute destination, arg
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  if #out == 1
    return out[1].message
  return out

seen = (source, destination, nick) =>
  nick = ivar2.util.trim(nick)
  stmt, err = dbh!\prepare [[
    SELECT
        *,
        date_trunc('second', time) as sectime,
        date_trunc('second', age(now(), date_trunc('second', time))) as ago
      FROM log
      WHERE nick = ?
      ORDER BY time DESC
      LIMIT 1
    ]]
  unless stmt
    print(err)
  stmt\execute nick
  out = {}
  for row in stmt\rows(true) -- true for column names
    out[#out+1] = row

  actions =
    'ACTION': 'talking in third person'
    'TOPIC': 'changing topic'
    'PRIVMSG': 'jabbering'
    'NICK': 'changing nick'
    'QUIT': 'quitting'
    'PART': 'leaving'
    'NOTICE': 'sending a notice'
    'MODE': 'changing a mode'
    'JOIN': 'joining'

  if #out == 1
    r = out[1]
    message = ''
    if #r.message
      message == " with message #{r.message}"
    say "#{ivar2.util.bold r.nick} was last observed #{ivar2.util.italic actions[r.type]}#{message} #{r.ago} ago (#{r.sectime} UTC)"
  else
    say "#{ivar2.util.bold nick} was not seen in my lifetime."

return {
  PRIVMSG: {
    '^%pseen (.+)$': seen
    '^%plast$': (source, destination) =>
      say history(source,destination,1)
    '^%plastlog (.+)$': (source, destination, arg) =>
      out = {}
      for k,v in *lastlog(source, destination, arg)
        -- Skip commands
        unless k.message\match('^%p')
          table.insert out, string.format('<%s> %s', k.nick, k.message)
      say table.concat(out, ' ')
    (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
  }
}