From fe7055298a956d2a2c56f487c7638ce7da040118 Mon Sep 17 00:00:00 2001 From: Horscchtey Date: Thu, 3 Feb 2022 21:08:20 +0000 Subject: [PATCH 01/10] Automatic group adding --- boddle.go | 311 ++++++++++++++++++++++++++++-------------------------- bot.go | 6 -- conf.go | 8 ++ ircfoo.go | 15 +-- 4 files changed, 178 insertions(+), 162 deletions(-) create mode 100644 conf.go diff --git a/boddle.go b/boddle.go index 86604cf..1e78497 100644 --- a/boddle.go +++ b/boddle.go @@ -8,8 +8,9 @@ import _ "github.com/mattn/go-sqlite3" type irc_msg struct { channel string - msg string + msg string author string + retmsg string } type cmd struct { @@ -65,20 +66,6 @@ func unique_int(intSlice []int) []int { return list } -/* -func unique[T any](tSlice []T) []T { - keys := make(map[T]bool) - list := []T{} - for _, entry := range tSlice { - if _, value := keys[entry]; !value { - keys[entry] = true - list = append(list, entry) - } - } - return list -} -*/ - // function to split advise message correctly func filter(msg string, foo bot) cmd { var c cmd @@ -125,17 +112,17 @@ func filter(msg string, foo bot) cmd { LOG_WARN.Printf("invalid query.") return c } - group_id := 0 if rows.Next() { + group_id := 0 rows.Scan(&group_id) c.groups = append(c.groups, fmt.Sprintf("%d", group_id)) - rows.Close() } else { - c.valid = false - c.error = "Group name " + group + " is invalid." - rows.Close() - return c + if c.add { + c.groups = append(c.groups, addgroups([]string{group}, nil)...) + } } + rows.Close() + defer stmt.Close() } c.suffix = res[3] c.valid = true @@ -158,6 +145,7 @@ func filter(msg string, foo bot) cmd { } c.cmd = strings.TrimSpace(c.cmd) c.suffix = strings.TrimSpace(c.suffix) + c.groups = unique_str(c.groups) return c } @@ -174,23 +162,162 @@ func getRandomEntry(c cmd) string { return task } +func addgroups(new_groups []string, msg *irc_msg) []string { + stat_sel, err := db.Prepare("select id from groups where name = ?") + checkErr(err) + stat_ins, err := db.Prepare("insert into groups(name) values (?)") + checkErr(err) + var groups_added []string + var groupids_added []string + for _, group := range new_groups { + ret, err := stat_sel.Query(group) + if ret.Next() { + ret.Close() + continue + } + groupid_res, err := stat_ins.Exec(group) + checkErr(err) + defer stat_ins.Close() + groupid, err := groupid_res.LastInsertId() + checkErr(err) + groupids_added = append(groupids_added, fmt.Sprintf("%d", groupid)) + groups_added = append(groups_added, group) + } + if msg != nil { + if len(groups_added) > 0 { + (*msg).retmsg = "Added " + strings.Join(groups_added, ", ") + " to groups-table!" + } else { + (*msg).retmsg = "Added nothing to groups-table! :(" + } + } + return groupids_added +} + +func checkSorter(msg *irc_msg, c *cmd) bool { + sorter_row_sel, err := db.Prepare("select id, tag_id, groups_allow, name_allow, has_groups from sorter where name = ?") + checkErr(err) + sorter_row, err := sorter_row_sel.Query((*c).cmd) + checkErr(err) + if sorter_row.Next() { + var sorter_id string + var tag_id string + var groups_allow int + var name_allow int + var has_groups int + sorter_row.Scan(&sorter_id, &tag_id, &groups_allow, &name_allow, &has_groups) + sorter_row.Close() + + if groups_allow != 0 { + (*msg).retmsg = "This command does not allow groups. :(" + return false + } + if name_allow != 0 && !(*c).add { + // warning: not allowed + (*c).suffix = "" + } + + (*c).cmd = tag_id + + if has_groups == 0 { + group_sel, err := db.Prepare("select group_id from sorter_groups where sorter_id = ?") + checkErr(err) + sorter_groups_row, err := group_sel.Query(sorter_id) + //sorter_groups_row, err := db.Query(fmt.Sprintf("select group_id from sorter_groups where sorter_id = '%s'", sorter_id)) + checkErr(err) + if sorter_groups_row.Next() { + var group_id int + sorter_groups_row.Scan(&group_id) + (*c).groups = append((*c).groups, fmt.Sprintf("%d", group_id)) + } + sorter_groups_row.Close() + if (*c).add { + var tag_name string + tag_name_row, err := db.Query(fmt.Sprintf("select name from tag where id = %d", tag_id)) + checkErr(err) + if tag_name_row.Next() { + tag_name_row.Scan(&tag_name) + } + tag_name_row.Close() + LOG_WARN.Printf("Alias used. Tell the user about that.\n") + (*msg).retmsg = "You're using an alias. Please try again with 'add " + tag_name + " [" + strings.Join((*c).groups, " ") + "]. Thanks!" + return false + } + } + } else { + LOG_WARN.Printf("no sorter entry, no translation possible\n") + (*msg).retmsg = "No entry found, sorry..." + return false + } + return true +} + +func chooseEntry(msg *irc_msg, c *cmd) { + task := getRandomEntry(*c) + + LOG_INFO.Printf(task) + row, err := db.Query(task) + checkErr(err) + if row.Next() { + row.Scan(&((*msg).retmsg)) + } + row.Close() + + if len((*msg).retmsg) <= 0 { + LOG_WARN.Printf("no entry found\n") + (*msg).retmsg = "No matching entry found :(" + } else if len((*c).suffix) > 0 { + (*msg).retmsg = fmt.Sprintf("%s... %s", (*c).suffix, (*msg).retmsg) + } +} + +func addLine(msg *irc_msg, line string, groups []string, cmd string) string { + LOG_WARN.Printf("adding new stuff: %s from %s.\n", line, (*msg).author) + + res, err := db.Exec("insert into line (content, author) values (?,?)", line, (*msg).author) + checkErr(err) + LOG_WARN.Printf("added line to table.\n") + + // get ID of content... + line_id, err := res.LastInsertId(); + checkErr(err) + + LOG_WARN.Printf("line_id: %d, tag_id: %d, groups: %s\n", line_id, cmd, strings.Join(groups, "-")) + // for tag and all groups + if len(groups) == 0 { + stat, err := db.Prepare("insert into brain(line_id, tag_id) values (?,?)") + checkErr(err) + _, err = stat.Exec(line_id, cmd) + checkErr(err) + defer stat.Close() + } else { + stat, err := db.Prepare("insert into brain(line_id, tag_id, group_id) values (?,?,?)") + checkErr(err) + for _, group := range groups { + _, err = stat.Exec(line_id, cmd, group) + checkErr(err) + } + defer stat.Close() + } + (*msg).retmsg = fmt.Sprintf("success adding new super funny enjoyable line with ID %d!", line_id) + return fmt.Sprintf("new line: > %s < (ID %d), into groups %s", line, line_id, strings.Join(groups,",")) +} // line: // >befehl <[groups]> // befehl ist genau ein wort -func parsemsg(msg irc_msg, foo bot) bool { - if len(msg.msg) <= 0 { +func parsemsg(msg *irc_msg, foo bot) bool { + if len((*msg).msg) <= 0 { return false } - if !in(msg.msg[0], begin_char) { + if !in((*msg).msg[0], begin_char) { return false } - msg.msg = msg.msg[1:] + (*msg).msg = (*msg).msg[1:] - c := filter(msg.msg, foo) + c := filter((*msg).msg, foo) if !c.valid { LOG_WARN.Printf("non valid input found.\n") if c.error != "" { - sendmsg(foo.conn, msg.channel, "Falscher input du SP-Ersti... " + c.error) + (*msg).retmsg = "Falscher input du SP-Ersti... " + c.error } return false } @@ -205,141 +332,27 @@ func parsemsg(msg irc_msg, foo bot) bool { // hard code 'groups' to add new groups to the groups-table if c.add && (c.cmd == "groups" || c.cmd == "group") { if (c.groups != nil) { - sendmsg(foo.conn, msg.channel, "Ignoring groups for the groups-adding. Well, what were you expecting...?") - } - new_groups := strings.Fields(c.suffix) - stat_sel, err := db.Prepare("select id from groups where name = ?") - checkErr(err) - stat_ins, err := db.Prepare("insert into groups(name) values (?)") - checkErr(err) - var groups_added []string - for _, group := range new_groups { - fmt.Println(group) - ret, err := stat_sel.Query(group) - if ret.Next() { - ret.Close() - continue - } - _, err = stat_ins.Exec(group) - checkErr(err) - groups_added = append(groups_added, group) - } - if len(groups_added) > 0 { - sendmsg(foo.conn, msg.channel, "Added " + strings.Join(groups_added, ", ") + " to groups-table!") - } else { - sendmsg(foo.conn, msg.channel, "Added nothing to groups-table! :(") + (*msg).retmsg = "Ignoring groups for the groups-adding. Well, what were you expecting...?" } + addgroups(strings.Fields((&c).suffix), msg) return false } - sorter_row, err := db.Query(fmt.Sprintf("select id, tag_id, groups_allow, name_allow, has_groups from sorter where name = '%s'", c.cmd)) - checkErr(err) - if sorter_row.Next() { - var sorter_id string - var tag_id string - var groups_allow int - var name_allow int - var has_groups int - sorter_row.Scan(&sorter_id, &tag_id, &groups_allow, &name_allow, &has_groups) - sorter_row.Close() - - if groups_allow != 0 { - sendmsg(foo.conn, msg.channel, "This command does not allow groups. :(") - return false - } - if name_allow != 0 && !c.add { - // warning: not allowed - c.suffix = "" - } - - c.cmd = tag_id - - if has_groups == 0 { - sorter_groups_row, err := db.Query(fmt.Sprintf("select group_id from sorter_groups where sorter_id = '%s'", sorter_id)) - checkErr(err) - if sorter_groups_row.Next() { - var group_id int - sorter_groups_row.Scan(&group_id) - c.groups = append(c.groups, fmt.Sprintf("%d", group_id)) - } - sorter_groups_row.Close() - if c.add { - var tag_name string - tag_name_row, err := db.Query(fmt.Sprintf("select name from tag where id = %d", tag_id)) - checkErr(err) - if tag_name_row.Next() { - tag_name_row.Scan(&tag_name) - } - tag_name_row.Close() - LOG_WARN.Printf("Alias used. Tell the user about that.\n") - sendmsg(foo.conn, msg.channel, "You're using an alias. Please try again with 'add " + tag_name + " [" + strings.Join(c.groups, " ") + "]. Thanks!") - return false - } - } - } else { - LOG_WARN.Printf("no sorter entry, no translation possible\n") - sendmsg(foo.conn, msg.channel, "No entry found, sorry...") + // have a look at sorter table if command is valid + if !checkSorter(msg, &c) { return false } + // new line for database if c.add { // if there is nothing to add, just return. :-) if (len(c.suffix) <= 0) { return false; } c.suffix = strings.Replace(c.suffix,"\"","'", -1) - LOG_WARN.Printf("adding new stuff: %s from %s.\n", c.suffix, msg.author) - - res, err := db.Exec("insert into line (content, author) values (?,?)", c.suffix, msg.author) - checkErr(err) - LOG_WARN.Printf("added line to table.\n") - - // get ID of content... - line_id, err := res.LastInsertId(); - checkErr(err) - - LOG_WARN.Printf("line_id: %d, tag_id: %d, groups: %s\n", line_id, c.cmd, strings.Join(c.groups, "-")) - // for tag and all groups - if len(c.groups) == 0 { - stat, err := db.Prepare("insert into brain(line_id, tag_id) values (?,?)") - checkErr(err) - _, err = stat.Exec(line_id, c.cmd) - checkErr(err) - LOG_INFO.Printf("inserted foo! \n") - defer stat.Close() - } else { - stat, err := db.Prepare("insert into brain(line_id, tag_id, group_id) values (?,?,?)") - checkErr(err) - for _, group := range c.groups { - _, err = stat.Exec(line_id, c.cmd, group) - checkErr(err) - } - LOG_INFO.Printf("inserted foo! \n") - defer stat.Close() - } - sendmsg(foo.conn, msg.channel, fmt.Sprintf("success adding new super funny enjoyable line with ID %d!", line_id)) - sendmsg(foo.conn, "horscchtey", fmt.Sprintf("new line: > %s < (ID %d), into groups %s", c.suffix, line_id, strings.Join(c.groups,","))) - return true + sendmsg(foo.conn, "horscchtey", addLine(msg, c.suffix, c.groups, c.cmd)) + } else { + chooseEntry(msg, &c) } - - task := getRandomEntry(c) - - LOG_INFO.Printf(task) - row, err := db.Query(task) - checkErr(err) - res := "" - if row.Next() { - row.Scan(&res) - } - row.Close() - - if len(res) <= 0 { - LOG_WARN.Printf("no entry found\n") - res = "No matching entry found :(" - } else if len(c.suffix) > 0 { - res = fmt.Sprintf("%s... %s", c.suffix, res) - } - sendmsg(foo.conn, msg.channel, res) - return true } diff --git a/bot.go b/bot.go index 19bcce1..afc9c04 100644 --- a/bot.go +++ b/bot.go @@ -31,12 +31,6 @@ func main() { if err := configo.Load("./boddle.toml", &boddle.Conf); err != nil { log.Fatal(err) } -// boddle := bot { -// name: "boddle", -// channel: []string{"#faui2k16", "#fau", "#sigbike", "#sigfreibad"}, -// port: 6667, -// server: "irc.fau.de", -// } LOG_INFO.Println("bot started!") diff --git a/conf.go b/conf.go new file mode 100644 index 0000000..ccccef9 --- /dev/null +++ b/conf.go @@ -0,0 +1,8 @@ +package main + +type Boddle struct { + name string `cfg:"name; boddle; printableascii; IRC nick of bot"` + channels []string `cfg:"channels; required; printableascii; Channel list to join to"` + server string `cfg:"server; required; netaddr; Server name to connect to"` + database string `cfg:"database; ./boddle.db; path; Path to database"` +} diff --git a/ircfoo.go b/ircfoo.go index 2a3e2bd..cb04aac 100644 --- a/ircfoo.go +++ b/ircfoo.go @@ -32,6 +32,7 @@ func scanline_privmsg (msg string) irc_msg { if ! strings.HasPrefix(irc.channel, "#") { irc.channel = irc.author } + irc.retmsg = "" return irc } @@ -44,12 +45,9 @@ func listenToIRC (foo bot) <-chan []byte { return nil } - var line []byte - var err error - go func() { for { - line, err = reader.ReadBytes('\n') + line, err := reader.ReadString('\n') if err != nil { LOG_ERR.Printf("Error while reading bytes from connection.\n") dead = true @@ -57,7 +55,7 @@ func listenToIRC (foo bot) <-chan []byte { return } - c <- line + c <- []byte(line) if bytes.Contains([]byte(line), []byte("PING :")) { v := []byte(bytes.Replace([]byte(line), []byte("PING"), []byte("PONG"), 1)) @@ -69,8 +67,11 @@ func listenToIRC (foo bot) <-chan []byte { if !bytes.Contains([]byte(line), []byte("PRIVMSG")) { continue } - - parsemsg(scanline_privmsg(string(line)), foo) + msg := scanline_privmsg(string(line)) + parsemsg(&msg, foo) + if msg.retmsg != "" { + sendmsg(foo.conn, msg.channel, msg.retmsg) + } } } () return c From 7ead746d98e577e6e8563a8a2521a8db475ae8f9 Mon Sep 17 00:00:00 2001 From: Horscchtey Date: Thu, 3 Feb 2022 21:23:50 +0000 Subject: [PATCH 02/10] add database backup --- boddle.db | Bin 225280 -> 233472 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/boddle.db b/boddle.db index 345a363d96747b55c64476e038342655cbf03b8e..6a795bbd1112e521ad25981699d21f35591fb7ba 100644 GIT binary patch delta 6357 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 Date: Tue, 8 Feb 2022 09:42:10 +0000 Subject: [PATCH 03/10] sonderbehandlung fuer kreisi --- boddle.db | Bin 233472 -> 278528 bytes boddle.go | 4 ++++ 2 files changed, 4 insertions(+) diff --git a/boddle.db b/boddle.db index 6a795bbd1112e521ad25981699d21f35591fb7ba..89fa358e86b32c4557233283d28e05bb5806c097 100644 GIT binary patch delta 42866 zcmZozz}L_qI6;a{>;MA;gUdt(J0`IM8xt1r^RY58Ffj7}VqjokaOVFtnNMI{J@-TI zYusnK4{>kjUeCRZdk%LWcN2F7cOG{#cLcXTw=1^^wJWVi%5|8suje9U=+^AzVE&h?y2Ij3>< za5i$5ab|JGa|Uv{bJ}nkajJ2ObMkQ3Gje?7c*1ds;~2*tj@29sIHq#6aFlRla71$W zbGUNYa2Rr^a)@xSvVUiP$Nq%<68mxX?d&Vr=dky)H?xp9j#tQ%REvCd|l$lAtQ$(qNS#2U=%#%j*0&8o;M#>&R>ljR-DLze3-$62i+iFeq}sn5#*V znL)a^q$shtIF+kOftf)%zbG?3F*lQ|NuHTOI$;{9VE=bJ;xlWjwK{_olH!~+Qm8VIFnL$3WC^;juBsIH)vq_MdK{6$^I5Rzut4RRl z@Y3Rf(zG;=CVpmydhz1KjJ#C7CO&2c<^0s*lGLK2)Xd}z&L&=F2Fb*{^wgX@jwT*v z2J!OJqT&*+CT?&rq^2k4<#RM~F*Ar~=cH!l@i%cYGpMGQX6EIk78fU#CTDOoae#s) zGchlptBIYNK{`7pu`DySh`Wi6nL)NHGdDN2C^eI(zKNBYK|UohIit9wG$V(ji3Ox9 zvA86WyNQ{ZK{hEfKd&S;IfJK(38b#HC^;i7F(-$!iIJH>vLrJvCAEmXi2)qy<;9$h z^&m$VC8cJT@HEyjGsqVurlcii6s6{IG}baRh?nH&<)yMU)-W>&CuQdIHC8j%Gbop( zrl*!97G)MEXK*)GF*C@PCuS67q-K_IH&!w;$d(k9mSiMmI2EY94Q67BholYGz(i zQEGBVY7s|cCNqP0ZhmT6DtluF*ojG%oQ>(s43fpk^%^o zT#c#B4APmo1!<*u={$`o%nb4ciAC9&dByp8oQ=uM43gQ2*(IgL9F0lL4C3iUr64Vd z%nZ`$sY!{c$=Mu@3Cs-Q1!;+SDeR5$kkCotXp957qBJ!nm7_5hl%6tEi&OdP8)KLm zloNB(^NTV|GIC3cIUA#y86?wk64TRDxf-KDIWRe+JT5OcQgb*P!0VVjxKxPK)SOg~Mt6`@Nn&v(ccU9KgKTk0QD$;B$gi%TsLBVi z*&1D#8HDq56L}k*nHdxdQb9!kDAThwIx#Z{7o}!!H##yi$QC6Ql;(l`?*K|li6t4S zxrrs2*&L1bU?-*)r*byhF*8WkCuQd3WTx{p+JefAoT`k}%-qx>u0|VX25C?j6=kM# zH(E0@$fjf_rsw4sr*bq}fr2bGFSD4v(Grx1Q*&~88ZDR^KG2i<2`FQ%kB+c^i$H859#s(~1&P zOLIX%WyH)NUX-7clgi&{$jqP$QdnACkeQkT$`S^kgq%}a!q%wI%pjbdlh56#2gk*86YnL$1~C%+)EATzayuTh7YK^c@J^RmIw#@VP13e@EKjGV+Yjz%qT(3fW9 zur+EjGYBUZ<+3+wKq5Ysy-^*^D$3<)R0DZ4wYWI7C^4s)vr!dfMoCd(UJ7rc3Mim6 zvy1Y}Q*&}s`5Kj(8I-G1GfRq-Gm0|PN;n#oK;f2_T9m@ks0b?9GgI?G>0g1FK{>6c zG!;|;rY2{zH_9_Jh^A(y7I8MpF*8Vl-JHtbD9g;Cnq8WjkpnU>F)y2|QHGg8IyEOX zEitczw^14tcG-z3skxcipe!#1vLUe)#FPa4Ej2Nlr%{5LK|VJzFD0=kGcT31QJk5f zUNXN3RLZkAih;_Q%$#D5Mp1A&&jXchBA`%CEY0O^6b7ZpV6#f%0-@PI3lk zBdB)CNi5IIOXX-3U}g|6Pt80$pQDkVnL)e^R86oq@_|xGVn!ZMBQLZlDac7IsbX*B zVP>coO)p9;KnI6kJ+doRgTB!rjOTvbiL&s64T#n4=L?tCZ!Z zr{?fCvO^L@W==|WQGRMkDtjXvGlOV(VonNwBP%n5YEgbkc4BHl0VJD(DwYyZA#eUX%%{sp^YBsgem4ltn3drNzZujf~)utUNP0qlCASftf)uGY?cc=Ow2W@zgUj z$YJ|j?gQs|u6$+&>6Fw`P&(wy z0~d^-TAm{pTn^;cr{-|xfJ8x!oFe{gW(HMIm6KJPm!4LXSjv$FN<4`M+64o4a&!4)Nz=5eHg(gvu+;YtCu6_QKx3rdT5l0nsX zadJi}s7b*AsyK4<({nOi*b_m%$Sf&lO8^C8c@Aehs5O+IT2!3M83(G)vr95l^VnlS z%8K$!IAcH!r2N#R)ExF`W(LvB?9{}~ucQT7L>L$toSGDQ`5!Q_@vmUu|H}V{f5m1# zg<1Ud+|109OzB1Wr3J-|)`D!z>WsztMJ1_4@ena)E>>nqs1Pp~3$r*=Qc+@N9^Y05 z1_mBRh?;tKCT3yAlEic_PDW-?rku>YRJKB11_lN${)r6y-}vA1-{-%^f13Xw|3?00 z{B!sxf}4;KVzZ7y7QZMb7c+xoS$=AIY7Pe{GlO_VVp(d@9q)Vw1_mo`RwhP9Ce~vttUMhI3=HN_ej|$(cMbysgBceq6C(o?%XbbgRR#tI zQ;0Onau!|=+wF_)GsP#c@qb}tWd6RL-GSMMiG_iIamn^@C+4}lpd`S<$i%XNg_R?L zi~lPlGxIOT>BkJ2J+^ZMFz;auW&XwgmH8d>Bj#(&rJePSQa~pFtb3Stl zb0o7LvlFv9vktQYvnVqs(?6z9OwX8ZF`Z*N%(R1PEz=^VX-wTr^-Lv98BDSKUl|w} z_`fpF-Od)mJd;rc>dfz~(p=3@_68PSjzl<{l_Oz$uoLqP#_iienO(T*AwJ|{U|?ir z;b36h$hwquCTlNiBWoFJHfsWF2&*Tn9jg(mI;#w;AS)}&FP8T#k6CW8oMAc0vXx~e z%UqU;ENv{+ECnp7ERigJEKV%uEZQszEF%10`CsziY@oA}H4 zv-#usgZbV0ZTJoNRru>A_<8sl_`dMHreV)ra$9Z=1tmj$GGo7cKr=F*nC!Hsn$DhZU$Go0Ln@65Un1`MF zH}`w)$K2PsPjesO-p0L}dm;C9?r!dS?qco??l|sXZclDoZbNQWZb@!FZYHj8T(7w9 zab4m%#>xoSc6-KXN|hyv2Er^DyUj z&efdrIj3-TaMo}Za;9=da!%(@WNM$jN|9MejD>^w5Az4+C(JjQ&oUom-pag^c^>oR z=@LTBnkF-u`k0!TDwuMa5*ZknlNp&<85kIsGcvLEv9NMa;bLXtWMpFPwdd}IG0nOA zwx3F6mSE&F=PrQ?wsLcqPXCj}EYI4)!pvPfT_l}Zf!T~Zce;N%vnan7cMc?xur{)& zbLVaU7R519Q&it(0d{8C9EX>^e+c#%3`?7FDoui=4wQhTI5c6b4c2E(f#n3aIy@1(T ziY1o$9CHS<9@AT<{9t}*mXY&58M@782vsAk|$3{Nap$Sf{VC_cRB z@O*{RycC7>)Ql2^6j0|(Atf_aA^h;3;*wN*1ziQ#%sd6}#G=I9)EvEx{G#IIc7{f7tj465CTBwf=~2UsVuj%3jGV)Jii=Zoa`KZiVEzm) z$jr}6EI}CQ)2+hJ(8$cdQI81s8HptdiOCry3XVm&sbDj+6Z7&EKpoP&)I6y36#PMLgPhW0BQ$OESYm*V7%dT`4DY!;{)0cv!B{pky8Mj)AB-mSpK5DhXRu~;ED5!CooaLOzx zR!GcM$jMAiDlP#zz^SwtGBALoGr3!youSbXsMWMJPH8UM#fv$o_VnIQrg45w`Ma3zJm53N^ zB1H-ShNTE1CCowNwAt|#M>@fG#^whlK)Xb7p1&BI@^30+XByY!c z%d#^xs)M~a4`6p@H)GdimuBZ>XJGrx_KfWY+bOoaY#Z1X zvrT2|V5?@!WlLZSW^=7)vtZL=lVuZNV`lxz`keI^>si+QtQ%Puvrc2}WUXP%XH8@c zW_4w?VAW=oV-;X!X8FeQlI0G|IhF$~n^~5z%wXwasbwi(Nn#0Qac8k)(P5Ei5n^Fs z{?7c8`401W=7Y?enU^uoWbR?EV=iP)VGd*VV76k`VU}YSU}j?a!c_l^=?2qjrhQBs znHDomW9ne4V#;BPV+vq$VlrV;1GT6a7`SKZu`w_)GBL28XJJ^&JyRFTJIBJXfqSM7 zg14S~rZ$4Nj(esSg144?rY4kkmW5$G_e>2iubzSREDOUr?wRUfE||BLYo;2A$H>5X zhDDuwrYeZT1mZBP;hL!e;(?_sxn?TEIjmeWm7tu{EJ55e6+s*(2G-Lo46C_kDj;~v zxM#{kd8b$y>Q{5mltXY=anF=R@Ro7Ul!5Y2vM{XVo+*vst>B(1h2Smco+*joE#sak z0p*=wVOYvFQyj!&WMDnPQp`0|49>~no>?yn;xU1w7?yC&6an$T3YocP3d1?9Tr-8B zoZ~E(+%pA193}?V<17q|xn~L>c#F7Z@`HGcAUS5PnS5{#EB8!Z5Qm9@^%x7oLav!S zARc2qh-=9`lN-c?$W7py$pzvuf~3^BW^%$g%v>`$;2c)&nd~4A69el}7KR1fGuaTl z`CKzuK|Dr~oI3YR77z!ba30r8W)Kf7Wyw`PlL^ja<(|n1;xI9=9${ga%RQ3;!JET1 zqaMU#1j#XT&8UNOSh;7^f;dbJtcO_`W^>P|LGWgA&8PT=l^{+% zSc;WvMg@q+$iRAtMV)&_If%mq;xNqQno$Pgfu$_DW|YD?%v>`{;2c)&8O0zD69el( z7KRzzGl~$r>D)645xh3883iC7BS@usJ=ctUIM0%6Mjo8Q%rzqy&SB-Akpto|F|ZzB zVVK4}BOAe+$~_|s!JEQ0BNN191gT`^nvntLuyW5x2XUAfSogCqOyQo9#>T)@56ag2 zSr{gB%}51FGJ+)axn`umIm}!$lHnXy?iooS4if|GJ{E=vTr(0uJVp>toqI+Chy#(D z#5E%x!~;t)bIpi@b6B}&#DX|X46J)u80sf-&4>YU89`hPt{KsAjym^@C=dsta01ti zNDvRKj+tvl1f0XlH6t9#*~5~*Jac0^%`(}4;2c)28NN`?E|3vE zAPy4)>n^Yn-Uwa~*9;!qm9mHW`VBN{W(9Jc&4a8#v z@hrJ!xWYNCTr*stoE`50w75gLK)0 zc#I6J+gU8RX4t_wtlTqfK^!KK3_~aP3>yTmgKLI0h{wplx(#H66`aG$HNz6lvE-Uz z0q1CO&oBpZm_T|M+PUgyn1Q&A46Iv0Mwr4mtXwlp;2cZt8O9(E6G$0D8}|$&1h18A zh9QW@$iTVaR^K z3{6}!VD;BVkUC{pp|%mE5L|&VF|clAVQA!@0jt3_vM@Ao%>dV6j3Bvst{LD8jFEwL z1IP?;^#$gza?JqOUSLig*9>sw1?JRp%>dV3U``F!3~<%O$iTY39%KTz>H_gtSh;3^ zYc4RSnrjBE;#$vA#We#~ajgUC0oPlM46N%wdSKPoI+jYV8L(<=9ZLn*3|O_b7NiGU zZ80*it_A4<*IJ=S_9GquC*8$Sl56wK`Jc}r<7|3xXuF0 zlyJ>}RavV+dSF%7YLFgSm9?5BlWPXJ%3@?-UColiHG_?fp`M8m%q!-a0jsfAvlMa7 z0M}TI46Lg_#=$DARUqSF_0=ktLarI$`UGty&ha!fqC3q(_xj>N|rpX>9ERbC5t-ObZ}(_R+h^(9adMZ02v3a zs~8zrSAdLzRaGlka=50$s;U(r17KCv3YJ)|>ENo0k%4tNOFc8!bZ}J#=CN{32iH_! zP7c>}SVgs*C7WwHte#rVV#+lgR!=PhIRRErEdx0LTu(7Fur6cC;+hVwrobF^uIb=f z3e3smnhvd`>RFe9On}u=OF<@pYbizs)}7d#P<-bsh_IJ=b(_)dc3Ta!rTTO!GiN0;`zj zv3PM!hgD4TK#l`fOpFYyb3smk)k||het^|Wb6H}zro-x`xh&CK(_!_}9F{1q>9Bff zdsZLwG)5`V(4!2~WCqs7tkYS0SQ}VNSTk5-SOZzzreAVo*5vnLabz)rj485k@PFCP zA;7H6STD`V$H~O;jpHT9U5<<3zSwD zzhJ+^eu4ck`!@Df?DN^6Rdk!*E9b{8qwXz zyo7l=b2oE6b1`!|c-e;&vl+7{vkbEUGYivqrdLe&zzaWiGOb~n&or4Sohh2h?+fD@ zCMMSFEDQ|HiA+r3{ua+V=;+yb7FM2(kP&QFP}hs6el3Exo@Wh$vyNvqm;=>#jD?w} z6D+_4ZmRRNBRKPU+7O(1JS|WyM_DX+phL&3M_JT)TH)fXJWWsyN9tJ^=JGVccq|Na zcp9L}jIRxj0Jq0^a-c>XU}2cblMPkY$70Dn8P29`h_>F08)kbWiz#=Ej87D%-=7!xIi?v+@MP*~~m4Pz|7_JWn8k)58-0 zRkfSNgeM5jX65mNvzdAPp&IHz&3GOkC~p@FLl2KPRM{>T6CPhUo0Z2C&SvKEf@%OY z;CbAk5<6LPcs$^2OCC2ko0Z2EssYqQ=W&Ee>;R>8XE=wI#|h46=5c}Q*v`Vx$zzM) zbnw_fRc&XnY-Sz@sD^DU4DCFy>91`pmOPeFMcY`kcr3VCLG2_a)@>}T zJg_;lZ7j?@)==GBSs2=Qj1inx9wVsstt^&2CU7<@4|EC*)Y|7UgK7XZx_NYtnRyJMCT(V6Xy(y?N^E9P=h1|-S$VYJY-S#9 zs0L7zoJSQZv5AG1M-9$q=23?#0yW3$d6W>m1|CJIvW+bDJPL3&D~~dq&CCOxhGX5p zQpY0;72m*8%LAJ**}zi6BMld4<&lH4nR(=)?DZ_wJQ5&wJtGtAdX_34aS#X0X62ED zvzd9MpzL)ll{_MFb_I_xoXyH33THF(h(XzFS;~0?;OsIUemI+zM-a|t=BXEga@Mev z^6yNo0a=JoXyPr1Ik{`lEeKG z&d%ok0B4(WzlXC~xj(_!^(@TXpP`&(ELq%d;OtE9S8%pE_iH$tmHRE6&CLA{%3jKn z!Tk)*R_A^WXR~s@fU}voUqabSSkk#4bF(tlgQm%su&{DJflDxRKZUXvv!ro9fU{${ z@59-w+z;VwX6{E&_9B*4?mKXH3ioX|o0a=6oXyOA56WK1l3dSy1I|g}z7A)za^Hlr znYnL4*$Y?_xi7=nqTH9@Y)ao2F}|Fw3(}XJO^uFx?@A zIZ)l3dl^)I3JWXuawvN;ix>A2IGdAu>GTUJ%<|Lsr!Y&@Ph@fDo)49q$YRMo7tUto zo(E-5U~%W3182K&&xErrxo5%ItlYDq?0yzk?rCtg3-?qw+md@aoXyHTgPV1FTPm|& z3}oVYIRpPn{#1TWz6E@)yzhDYd3|^Vc#?TUxleLeam#T%u9Hes5{WW#uv(STw3#76J>M%M0Nc811M z1`fpl(BebTgmqPEu7YD}Nj`YyugFRv7&OyWoSCYSn+cxkOU+eqgse?0Ni7D8l%*CG zmn7yvr@S1KQi~MQQ;YI+6f*M^%2RVd%WD<9^HMVlQWerb%johzlWPizdG_^4^L_l? zLF^1ooD3X_zL`Y|C8dygWS`X1l2nD9)XWqe@H`!8R#73bG%Y=~=%s<@cfLt z(!6wq4A81BJq6F?3{Ymq`yQDz=wnlx1bA`hAtPb|o+Pc2f&OH9sC@JK8zMz%Ss zJAj>`QJ#TA5vnp3Jl&ZFQk#^TnF5-r%uH7(OD!r-1%*3!!Z#(gNCDzd&=hWZ2`HQt zK!P5LdB{_ljpE(@>KVfoLwy8Q4~^sclGMtyZeMnWMso%Z#h}!bRE6@yVg=CBcF=6H zjzUQh$md|e?3~0RkRUjyLCX|U70U8KA(pG)od{ZWtfP>hgz%a|N+L)#lF!1seb^Zq zB^fvrJra`?azRtlUg@A!hDG2BbC91>a}@&867!&|Z$Z%uTC)IJ5}~Kyl$ocHmRMY( zkX)1sTC9t_$|I@Ud-}>>2Ti4CrYhv6=H-^Z+qNiTcBPKweJ)t+fFy zqX#8bC(wFg1>eN1d?bCL-L6odI%kw7XDj&S7Zqg|D`X^=mMDPN9aNR(D!3IL-jh*W za(GX2HYC`biV{m8>yGn41zk#Js)B1~DpFdmugvatVP|M`g!(kKC>!Qi9fg#{;$nrA zqSQbSbYfC^Dkx>;gO*67<|+i2l%`~s=7Ivq5ws>3x zH8?pH7bI3?rWS#hvVQgRhsi;Gk1VM~Zo6@0); z&ykFY>b8MaFyV=y0E85Dc?w~m#ptO;3fZ8r^hwPu0WDT32QPb807VEmG*gQeK+9J^ z3tc=CGx8L&^K)~NizC-=Yc_^*hI+&zIQOE&Qn25_RWl?Fr{!dV7TthM1qEq9Vo^4D z#XFKKW4o=`85&iQ(hJ<6lF|}|^3)6k@OmT20-MxaXpNVr;GJKPrcj@Dcu5YZGy)e! z3fL@YWbC$NXJ`Z!l%R$~2B=^L#eI=N6u7D?fGoCGNJ-682+J=5<(b5Eh4fU=im4KX z%nWEig+l@;H#MV3p{i6NJv9l`)Tqx(Q2@1@K#i)D#G=H~G|)1jbWjMVry{39t8NQ6 zhD4ArGgB2ZKsy7#%_?|g1eF#SgUdYM{QTlOd*F1XLMBm4b>yUaEo<$VnhqxE7`4ffq?4dB>sK5Yhku z6>MdhsW}SenW>OQsDdYas|HwiW}bp4Y_|rY0Z^Y>8QX0D&3@jX91cpo3NDGs8IX+| zAb)~fkyxas;F72SUI_?FXHJPl*{P`osYO-_>Y(reFMCZaO)Cbsa`n_<9!#wa?$&2# zXcPo_5T*3ZO3h8oo6cRq$XV~=>8IdmqTmE7WJ^+u6pBF`MW6{IF)t-E54>FxVPr(N z9!edES_KA!(rOxLog&Dg;3bOS=6qVJ0w`-AUIJMk=zDlcMoJN=hOAF5LYSS_tqW}_ z`lObWfcBn%T6mDC4Fwgu>8T|}sU@j-dJ3RG2Q_R`^B@g~tkjaK5^x8`H8T&iU=zA# zx3nm~AQ7~a1*JTQ?AC#1>3Wz!kQNds!t)fsS+E>bQ51t#Q7QyO_q^nSmpg*i9R_ED z`dpyhnZ+52IZ4Qkl1j&JZP>EnJdo@1vLS^{sIF6fJ~(6@bHTkLP!QGUr=_Llfie(k zD?OxJi=ClS7!-4$5(U(HbAoTJDK0H20Ih{A&dvw<8dCg0S7oNBCKcs_D$CSj1sBME z8+$|u^D;0naxkfbc3nK-zs`S>zkWCWTK)z6llfcu%lR|;qxpUL9r%s-Rr$sFIr)C` zz2kexcZKgb-%h?YeDnAw@HO$3@@4Qv@%i#O@EP-|@k#J;^Zw<1&-;k?3h!~=oxE#! z=kZSDZQ(8F&Ek#W_2+fsHRV<3mEz^)`Oou-=PAz(p3^*gdDinR+{d|haj)fGz&)9}mAjlfi#vwfpWBJslv{&anwyWC zk?RZBGp-w4r?~cVZQxqWHI=J_tC}m9D}gJB%azN5ON&dEi=T^$^DF0b&YPTPIQMgI z;#|r(owJLxma~8}nKP8rozsd_ms5eWUWk*G;|IqpjyoLZISz4bk7DndJ%+Hu_FrQ}L$Gm}gG4nL$4(2N69OgLYKxP+aGiD8D zDP|s~e@q{lo-kc!I?1$$X&uu-rYTHqOchM^Sxm7^0Zh(JrcCOP6`ydDdp3A^4if_# z3kxGF_iXU`93}=fW)?;UuG!E9I&91=mRz&J3v?JkQYBoo!RvFt9Chy5;N>}BO$?W~ zXM@-0fO(88+_OOobUB$#lxsG0g$^4N z3qSX4@CqF!29OcVT(dPHOMciGSt_|^gO}(qGO#g%%!97bVPgcD2VSAW1d?H>@8+Hj zU8BRs$incOdp3BD4n(q_dp3BHDVWE|z&#tfNQaGqg^`JCHh7T^BLf=)$O7mZ9X1A# z1>iM0OduJCb=-0_26|n5H17PY}i7b|16nYvtbK${M${|{$pYI&ovviQs*DY2=GcB zh}1u>*^s3=^&pk3+_S-Jb(k1f|FSUr<(>^*tOMcw;hqg%tpnlx=AI2+tpnlpbIpb= z*ZIq$&NUmhT<0$fGuLd`a-F}R00A%8VPatY!@}^3dp3Bz4un_#lWR6?#m*lVEw0(H z6+3@GX24eL`~i6ryvCG~f%P{ixM2%+euMlBUa-Ril41D4H5;~K=Qqd%*ovLsAXk7_ z>@YE~{$gSH&NUmnWCyec?iY(X_bl*Y42aw}u34}}JHJ>gxn|WuQWfhj7G|zlur)is zSXjAdf!FLXF|ht*VYtsVs|u>_Crdo{EbyWoh}2iES+G?*KSAcfR_**`Vdkoz1zWcB zlZBOQ7Hrwh4;D-AS>R;=?)VE>o*pL z58SiBYj+^L_gu4JYj?hZbivl{d;?hwTf6fWWEXht4if|GR~CkM+_S)ocObmC^<1-H z%Xhwl%z!Q5`3f>45$gCaEc{%vV9R&DfPxUTdMSr}e& z&jK&uVPatY$ilFcdlq;N4}|xEYZhz~&qt8)uthu{LGc4#!^6bD`hkVvIrl8^A|43u z8TTykDjo>$Dc3C6GM*2h@PsVmsR!v}=9&dt$MXT?VAwjI_be*hv%u?km>5{!voJj2 zo&{dW1L5uEngv_Q^B&|VM`#ef2RRD7z?6xB^&JbtWA0ht1*Q<*I__EE6{Zm0qk67c z;5DXvXEb&~kU`tHjf}8R`%jP(tR7WXW06~@E>QaFce z7Pt;$1o4=;W`V0PFo%_E7N`aTCBxUC$OPA4Obo2ASs3nd&jMFq5Z+y`S>PIs5hSO@ zH49pSvA$-h;HsYmuED@k%v`ge6&UMlP#A(MFeV1pS1b&7xMo3XFxFQrqFl4UH5emE z%93jqxc&ljn7L+wt1mEzm3tPn_F{d>!f=~=7PR&P@osU=g4AC146H9fp$4tKSYLuH zhE`v!FIX6Ea?Ju)UyKZ_FF>Az)?Of~8(g!%l^0lwnQIoT?s@?-0$g`7F|a;oVQAx; z1+KgpK|D+DS>U=0B6Xdseip3udJb|1xb^}oWaXX(uDqBSSf8;lT;rMruDuvRJWH-w zkje|hVdk0zuDif8tlYC;Ro7D%hO6AOU{%*s7KSU_vtU)%Qx=BHT(iJc7byOpf^3G> zT~9$709JQBVaef|1+Kdo8Caiyk_ou#0&`foX2B}1CoIlfvtSk1V~{df#q}5z7T}7D ziGlSo3&SO@S+JVxF(^^9z^a``EDRU9XMw9OCI;3=EDRU8X2I&NM<5fybr&N@ij`{? z12n5WVsYl02{r;Olfyj|TyZfmus&pAIL|#3TysHq=eTErt1bxdEZ0nM-33~M`w+Cx z23&VRaGW%-6!C>iwPvdaGHB2xbA}RPI1o!S6&d_N$#1j z+Uq_G!wIgL;M$9kf%QHMC-+QP<#nHhq5c5ZOjzx84`dsx_PWQy%smradoeMv-eX}n z&OH-ceK9ex-eqAp#yt~Ke}Q>Nxo3i_FNoX`u9>j<>n_MdaQ(#yQfSFF6I^{UGO*rZ zvE-VW0b2unhlQ1UCb;@y0!c9(=9&qszizYeam@tRUyKZ_w?XP))z@tnhC^I4VYSz7 zmO8GPu-fYu$X;0Obqf@3;M$9cf%O&(!$Gc@uOj!MO6Xd2?Xw=>W83C@p z7#Ub^f)VXJZYcD1chhab0Ojz}G1LRt8^~J=%dV_^wAJ(v zzpk<{?BbpYtG}+WFzn=>39i4G7+9~cFzn!(38}z9Jaz7w;Q9+9wVi7wtOC0Naum1% zV+5&V<(dhuzZe-QT!}FdE@YZq9 zgw%ql zKQhl@4q*DjG=s^C@fYJ3##}}fhSG_S;`NQ%-F55?O`tg&$DCq?(%kaYoRXx}^3r0^ zsb31d;4u~j@Z7ipXbLZ}G&uu2+nuVAo0tQgG0jzQOUx)LN=#9xDlJlQ1W%CYDL8_Z z!p5t?bEOKUxy2=imq6!_GmA@fKr^Dn3ND#>>7d~?(6F;NXb7eVbPyA0Xt@A9)rd6C z=hj^dol!v@I|h$^rl*1rXvs`ZEdmWDgAee6j66AI=7W#N0RBx+E5*fu|wCA*bL6I)F?eFTXxHLm@n~qzV+h$r-76I*?iLG|)U9 zWJC``DahQ^Ot3L-IiLYT$Wdtu ziKS_vkzN<@d=l)yG$gZvx=Yy^8u_6P1Wo6pLKAm!J#;^F5$G^Ag`&jLVvtFGsTrWD zWYFwJN`7(%c=+BCa$p`JMOk&1K&Lf8V}zj0;+$WUpO*+)G*ObOqmYzZoLE#0wkQ=c zJctx#cHPC`E$)yF{YjvC@|>L1JcR(zF=VNEATNPOUIQ|#suDp?@=h#BEkd4EvF$Em zV@L=23A)7{GS2CenVJOEovYxQ3HFXF>=-y?{jS}GYz*b#41qE_4VoD%$uCJQR&Yv8 zsszn?LMFgLj_^Iatt>UKATv1|DRi^D3t+Rr;JLQ4)S}EHP{shK9QRbv8FD453dN~8 zC8>Fk>9O&s~kaP^16aybw2XZ}}qXXKq4$_#ES`RwL3q11g1S$@y zN}fW|!t9mSm!wrjXH{!_LrX4GJ%~(K#s!xuwO$3b~*IEg)01IiT4} z9ms?uXuumX7>}5}Pb@9YNzKei1+8w%$pQN$Cl$0n0D0|=cXu|@q*XC^CKwh7;h;qd z;F%y$(1YVkA-DeU9!PNj3Kr057>Q}1V1b5;cXt-Damg9Enc##1PFVp(`QQVtKte^J zBwwrmIxYd^GDpNwf=H&NcW1IQG+KfJ0vcZ_iS@;xd|HwU3(R21IfEcWyg?zL;Ftkg zex;+32wQ9620AJe94nxll3tX5cs_WV93{+Mx--}qK+DcBCO8y|L5CL>CugJpk{|fQMjhz- zD{vbGR31YX=P4AYW~AyUxFr^W4h@Br{F!+Q0f~8;$iaIe1MUXn7xaHXWos4SWu1YLP;HNMcb5s7ftKEGR%;g5cSm0I3~8 ziwVICm6B33a}~VuLFY??8VR|OrGiPBdEmm$1#~PA_$11r3{Y)fgcRhu-SKP;?w}QE zMX8|aFvyf1cZL+rh|$tBx4$7x?|WGnv|#Stzwp}4}hPInWv`^3~K9v8WG?G4r-t|f(q5tJSzqD z@J!HJhrB${!iKzJ1;~VEadJj}ox*(gt;jsc5lY3$8F`r{ z;PzxHWM)~RxB#@yGBpqE273iiYX>qRUsak5ieyl$0hFop6pBj{L2I!TJnF%VaFRet z60$ZFq!hf|C^b(3v}Pj@>J*Q|OG=AVi=Zt}giVLHfr11nc*DD+psV~`GE)^mE9O!a z+(FR^TCtU?P@1a{ln6O>6cqjSpuATEDO*8HDicAqd`2p$!;qd@l@Ch&h_bHHuRD^R zp_v`jSOo=iX&PwZW+r%LXlkB=rVu%cg1wOk-N5Rlc zN5R-gPft(5Be6sQ+z^Jl0%R6wac*%*Vo6DQY8t3*i`@LK_vnsbW2goB2(mD*6m(oO zC}cpX3UcbI4mjIq=NFfxmMDPAnZt9EQj5w#y@ovGwPo?$;p_~Jnjo_hixmR$i!)0y z%i#4EqTS`H(890!T!oUN!%INbL`o?%k9ef!&vS|A7|%|gHC&guj&tqi zTF14JYYKP9P%bH6MdU)L*&i9;;Ij?h`=G@1*k#h;>49*_Tdd?!wG|nha zKTao3GfquT8BTtVSDefo-#FHA?BKY_v4G<)#}STvj%to69QExSUL1BD$sA!EsvMFW zh8&C>yzF1tpR?a$Kghn7{Ve+&_7&{?>;ddC>`m;Y>>2Fl?9S|3?6T|vY;V{eu>D|X zVY|$BjBO#?F1D#`YuReq3fVf@QrUdjBH0|+jM>!K_}HY_m{`BE-erBsdW3a5>jl>N ztgBhuSSPVovL>?T*0YANy0hxDTC<9?DzkF2d}8^>a*JgP%QKdY$vo{m)jWATi9De^o;`}v#s%lWhU(fMflnIeuFm= zM}rTAG-K9cmIZGj{=xKy={_H1eZ~pCJ$xJZ7K67G?*dQmPi5-l7Us6%)~)Ay$n}=% zCpR1OZ^jLbOsrp7Sb39}e={y&WMcio!q1xs-4yYK#gaFH`8Q(^Se%(R9xl$x8^`>c zv4oL{^)rh)FK9O>L<%(R&ASlJX60P~-6RK^q2`5c)TsZ@lF19(o$;T=ju*CT<39^4 z?+mC8&|Ed|G$`jU3qwC|Kg3+tzbxv!eQ-7_Zx5W!%-ajq0Gd{S53)}nrgN5NUFKjdM z4;F?`yhTtQKUgezOW|x*-U8U($sa7tyoFHvK+~HK>SuA<;pgO*@Xz{{!AAe_I z<%R9@eFr+R)E=t)9Sg%FUf3qicPz}jwou*gSXg;sn?B#NsPjU%XV!z~%XncMH{Y_v z^TKv_zGY$NwSek=%fiZQ4z&U_SH=t5pZSJ`;XW^HIO}W z@hT%Yw|JGH_Pu0b=7sI_e96Mfs{mC5niJ!NZP9$eV#zCqkhsAM+w1v)g_#$;*AujN z^92hluQXKEa~6g+Uf71s=PZ`Ik_d_Gyx{Ggj7+T0S(tfYyFH(?u=0vQ6@g~Lcwt*P zpRri-!nS)pV`1inZTEb}!pbWI)$p{Qh2bi%AdJVtaD`U@!MV%}+w%F8g_#$&Yx5}! zD=%y}=M$D3Uf7P$CoGn{upOUISXgW?E$^T z!pZ~N$9apzlII~*187o<2et|H7E1sRY!m2BmU>Gb*iO!yEUY}R9iBH?IC<_sb%W-! zcwpN=Z?dTK+(Jm~=YehUyurfCa|0>?n#?1fr#iG`JC zA5;P~N5!)n!P&^O3&B~>vlGEt$Fl>$SYS={v^_-IH0o|F0e4H z1l^1qA#TOQ4UfA&zpjAPju{iR=4yX83&tl07JDuVa3o9?|bc#^6hP|cwAK)m(d z2;MVZ*pU<;SQwu2!p^Grz{1Z9JCfo93o9?|NQw_E%)IVU-Jr!kye#eK@nTBxdXp!9&{kQ*k@ zVK{4fRza144j1KFiQuf`Splnm&ayD9W-ShG!|%(PvmJd0<EA446AwOBRH#g=J9~e4q#$pJ;lPXjAt%X z3Ur<*&m0721|mmwEX=&+u=EF7!^sOfn&>+VGcWAaAkeBx-Ylpc-&h#l^JYTL z{l>z|n+|6)^JYL5ffiBnrXo0Rc~d~M_LdA)4E)FVoA?d*F7TD|sqtRnE#?)g=Q+U> z!^6hCh1-Mc3s(o1G3QmzYEEg6g&Yy=Z`hmJE!bYNO=YuXeaPC%D#dbvrJKc)`3rMA zvm3K2({rYCOq-deGZiseF-bGNWxUF`hjBh*3!^8aAtOJ-4Tc>I(;2Fydd%4vK$FM8 ziP@>yMTeJUq#_2?}LQHy0*cd_?I21uc`I*I_ZF9K_PQKxw4L`-ut$CpFw)FDMVx(CM`5t3725klo z#d^^3)1bkXocwgq7A1tuqCG}z44^%-1x5MAB?xhg9z!;USOyNoq)gDTRiZ+AVv#~# zY6^I~EGIQZp*$0`NjR~z7_>*R6j`f8j{zG4XrowpW@3s$abjskem!iuU8+YP5>8o( zpmF@7)Z}c0onk$DYz&}{%f*SMNyy$8?a_reGCeg9WE{*i?H(OA2GC%CqCygA)-by$ z6Ew688e}U1ZIn?c&4YwSJ!rFIrA&`D8v|&UU#3DXXgfYw6T*7a9xXNo&@TMqg3P=; zg-nIik}AmTWk!B+i9&i&eu_eQez8J&aejIU(iE~%k0u)f=%DhXoYKUU%yiJ+y1cx^ z?9_rZnBn!Qm6AOgYz&~C_F0JvX+^1-IhioY)JlmSbx4STmR%_1CPEwycC%QI8ul=; z?onl9NM_(rEY1gQxdM%eBO*f~J2f{q5gcEcdBqAT{`o~|pnbgvE9H7rAW0Im12a{j zpeR2nCp8x-(TepbLo9}bEkb9bV2{%D{sT-CnVotRr@uMIJ)FV2*;}laSvs;hIbels=?o2(x(_0TQl{3fo z2u=TTiYb;kyhm{Q|3gd>Og#eABM&o0GY9qXPY*fG6vUj_!#92VVWv#xz#iV|D^D{8 zGH3SiOjkb0l*#Ph!#(})X(oT>;2y5&%14+&n0h#;R~})?V(Q_T{`3fwH*-`E`*i;^ zOi|2nJ#5pBk21wE`}eR;Uvq}ZpV_B}W%`t(AjdLKe|(fFf;qN_Y5Ko2OtJO;J&e#X z*YMPI(ClQ9l|n!o_!=e9MSQ763T63upq=+AX`rJait-eaQq#ffD2l*`D5R(6<>%(6 z<{`q`v4;V&i7Egz6_%Hi4?Z|Q9F&DYvn3&5V|4vWOR7MVRG@)d@B|ZRehp!Wd3QbJ z4E~VAdx}77OBB*m4{u9L%L5%~5R?eHldnhtG}c|5oPj*M1u9VgGyMkblX}X3gZ~u& z9{zRw3;Cz;xARx>=kUk#2l6}foAPV$OY!sY{j2Bu$oGWrI^RjY-F)l#7Vu5xYvZfn z%i@dS^XGHoGv!m~lj7s${m=W6_X+Pc-jlq0c-QkT;+?|V##_Og#T&~T0Np1g$;-p@ zpXVdbW1g!#CwO-AtmT={Gm)p6r<5myCz{8X$DYR!Jp9PX{hRwO_XFEyB&l^^@xj*FCNaT!*-}a4qMW#nr=A$5p_U%oWDv z!DYpz!zIrp#Kpq-o%0pv9nSNdhd8%zF5{fR+09wUS-_dZ83NuVrNb%DDa6Ub@g2NL z>Kw-bj!hg(Ic9Klanx|+aU^ntaJU{S5m)_KoaI*r%~~ zuvfF^vL~Yxqp3CdaY{zWCtjsLN%)#_~V&m6(uDQ_FZES@s z>fCdotJ~NLSs1Ok=7LwZF*2|fvhZ@v1ut#`b6C0Og4eb&F|ZY|Fj{fV1ut%61o3#e z=7N{DfjO+4bHNL=85r2|nK|czmuE9Du;p=a&edUKU}9uoV5`sN;+zX!oXx<%mczw4 z7rZo^fq^ZXi*qh`Wi|r?TNW4RT=2qd1_riFF3!2&W!Vf2Y#CgfbJd^*rE_u41ux2G zU|>t*;+zX!lg+@umdeFB7rZ2!fq^ZBi*qh`K{f*eTQV2tTt)C&Lk>#rFY*dn;1cUo$YU zg>!Mv1uws5U|rT=2?k1_rhu zF3!2&b=M3GY=K;ybHS^w85r0CxH#v6*IY9&u=#Uw&IPZyW?*3Rki>(kYpfX<*qpgI=hlPQ zSTiuNIdO5$Wq=yw$i+DayuzA+fz5%7a}IccH3I{iJs0PkTBsU3F3vgN#nlW9Y_?pS zbHGch85r1XxH#v47gjScuvv3)&H*p0W?*2m;^LeGURBM&z-GzCIS0I`nt`F7&4P<_ z4tPy90|T2m7v~)CifRT1HZv~HIpFow3=C|hT%2=?p%$BPa?SxSre)SU0WY6sU|`eYr`w9Pq+v1_m}YPR=>tb<+$CY^t1`bHJ;n85r19I63El7fmxTuqoGba?b%T znr32PQvz)XoC97p%?RRIa?OD*nr2gCVda_=2XZ-BCYE~+c+E6e6T>#{Ip9UpU>>6- z_Z;Y|X*NZ$fzVacY>F(FTywyyrWqO76j@lg=R|<>9}|ejXu&lHylfgQ7sfpYylNUE zWzIbZv}~G@iGfW4Y$ABoG$V*-$u$SMXqrs{WFmCYG)SEp_Z;w|X^1XU?m6I9(-58s z_Z)waXP6l3+2p}SLKjZ6$%7mTUO3Ijz$One(g>q|TUY4s_i#n>>pf_Z;ZDX^@l= z_Z;ZDX*M~qf#7x1j0|jYAOoSxrrG2`!3bV94VKB{o>T7%HC~Q|(U5x%c-=G;NEf34 z_Z;xTX(k3XS+I%FjX!L%AQQn0rx`&~tlV>;>!v~K^ttCi*G;p@g0B6T170@`HiKau z_Z;ZLX*Lp4_2>8gF+EjuSl~ns&UT&*DFj6Y|gK1Dh1s5OCGP z2;y0C&4JY{QXoUXH48+Y4)+{z)dJyZbI$=+E=&w;l3+8y)e9qtXUR1OT)luftlV?J z^$SFu7S|k2a4`dxv*Vfru4BL)Iqo^&N`{GnO#*BnxSC-E@hrLKfa@7Bhn0H{xT1lm z)8w85u4*7W4emMM%7%%7O&n|nWcLq_Z)EL1L3K1&jD9I zObl$IER6LGTyww`5F?0d$u$RD1A#fL+;hNH5JaI0_Z)B~1mP)j&jD9MObl!yU}u3V zB1RC;l4}mQCIWL>1~y@^iQpQE z5yZ3Pnggzqz#LZYIp8`8qE3-}4!Bl=@D#Y`fa@g)Po8@YxMpHvU=sqH2(FtLK|D*Y zIpE3(6#hb>@CR2<5QTEwbHEi869bzdSR1&8Vg&Ilx#obYC@_bWdp0=nK-9@{&j#00 zU>-vU_iS)I1?DlzaL)$UR7?zP0w5D-gX=0r5YMunYc{yH0`pk8XM?LNh(c-Z+29Hb z!js~j4X&~vJW1}^;5rM!li;2WuCBwJ12z&|r!j(fmRz&Jl^U4C$~7BYsew7_T(eBwJ4KksA zHmp|TX0ha+4Xf3-L7Qf0!)i5d7DfTC+2C4@5v0$AYc{M>;|94NT&XcJuyKJ*oDJOy z#Kr~MGYzZLxInw9!F3uVNS!+OY)F+>58^TMbI%6XX%I8`xMzbaH6{i&POypKT8$CJ zv*el$uGGLBR_@ubI*pTsk(X;WxK0C0sdLW;*JuzUc(`YSt28DCHV&|X;5v;F#H+XD znhmSdI6!U$*J)rWb?(`)DvblQmv=U}N`sie%{?1jr!g_Gv4c&7Rch>@@PO56>@192 z+_PbI8aoRkC--bvoyG>%<^;|{j0|jSAiH6e8XG7oV3isxSRuGlV`N}s1qCX&P6KmT zxn_f_G%zQSdp3A05EDolBM0|vaE%7xv2)LcRcS0>1EEz~JsS(iL|C210&+gAPGezV zWaHxA%qY&}$H2XednR`e_-4vpAN=3r<4 z#bL>z!y(Upg8d!)9`^O@581D|AUg*fy~(W1Gpgk1dTYip`g;zK5-zt%yyU&4i7gO`VO2&4Kj}>jl=stY2AQuufoY zVJ&A}$-0eo4r?H*D{B_31#2v;5GxzYPtf&fEGJmrvg~1b$g-a03QH&WTC_zhQ(3%N z>{tw0!dQ}77+54(RGGgpKWE`#-om_s`4;n8=Gn{#m@}DUnM;`inERL;>zU=4g_zBm zS(%-gwZMyMb~627dd+l?sh$4|(*pkIOlz1X^Ka!}$$yalEdLz-Tl^FG+xV;b3;0v{ zqxk*#UHC2e_4t+fCHQ&ynfSi*z2SSvca85f-vPd@e5?58^G)IFXMXygPW;@-AYkVJc)wV~XaT&fCM=$Xmvn&6~g*%In2z z&uhY~$t%Yz%*(;^m**4DbDldq7kQ5H?B?0Pvy^8RPd`r!PbE(-Pa=~ac+rvpj|~$O zxO2?65pt(3TL>55WY9SukU?aa9B8hBfe9=LRTs>~HwnrN;^LbKWd?HbK?mMJO=mvn zBp9fLZNUef9b;2u3F8Zg8n4L0XwC;cyB*XTX5fQPkFhDRSn@$nw`Egcsb}R2hH6n@ zVKn0lLU2s^pr^Nk^qcTOC(A&MT?Rhr{1~XY%;yIgFK3fyVde9MYL{nWH0Fa&kb&CD zeBKBNBR=Sq7^tOO&%g(r69cuB`Ji)RpoTIZ^!Ro*IhH&=H>h?{E13^^W;;l`0UvaN z4Ak~z;Db((fttvCPEcpbvas?wLbc1XFzWL`=fuG6W4?OmA|DWM9Utt%Vo>v#54y?+ z)HvpYuJU1%VPWNiuJQpljrpL9eAr}I)cK%`eAr}I^!O~GHiFv4eCAN6NV8b-nZent ze5TO3I#BCbjn4$3MVAk{%m>^g=7TQtVUuF9p%19#ticCe=mTy7^FbH-u!*x+@tiLM)bi(8J%^ zgjiVlpzD1=ZDBb+W~dIqdQeb6SNyOEvRLv#SNyOEvas?oz*Mm?%JM>&{IChKFm&)j zm;8WQ$TGZtp~^swTVCjjA2tCNOJ3-)?`#4rth~Pvillje!a4PfQoPVLKcL33BrkN$ z4~Qed3tjUAZq@Qa&v<9!XR+jkF8N{OXJO?9E%^bZVty7zao$f*llfT~#dtqLIiU6| zFZ9TFHa-?hUg(kUYoc7Iog&Pzg{|mKVC_hmDuT zk{7b(hl!1sg_Re&_V848alPg)aI5wPEWSc%iF)*mzhhc^^YFArA{H zFLc!p8xM;*FZ5J*HXasz-Um>lKBJ^h`Hi-naJy6T6Gi$$Fmy6Oj{fu9$;>IcN( zY#c0>ywF8JY#c1=yr-a{$ic$O3tA8aN*$oKDDP3I zQJ|(MFL-%RJrfhS1&FT&I?`91L837LYE4$v9bj6Le~I+8nL|lA#SW^V`X9Gg)RsJ9s0$_yB%sU z=maoc=;|Dh3&gSP` z4QI3RLRXToF|sf+@Ise$fDioQT?#ddk)@uYpLYqI$I#6ST?zs^PKhq0D$L zzIjk)92ehQD6>A6i*F8;9mB;p8_JC4;+q9!Mse}Ygfb(!_+~(v5nO!Jq0DeDzG+Zq z7#H7EC^M9cZwizdz{S@OW%_gR^+B0_TztJ;ptY(DOl-bfd_7QMA1=ObDASvZuM5ib z;^OOsGCjHYI-pDsF1~gs)18a24a#)m;%kL6UAg#LpiCDozGf)XnTxLp%5E5hq_ElxfJxR{&)i zaPs9tnfjc3c~GVvCtogYRLOP^KCuUn-QT%E^}kWvX!UB}19YoP0^pQUx@t!3W)P!KTDw$(I1lO-d}ReDTnQ zcWg>5v3zlG9Sqy}K$|W=^@b7)qa`17!6K-;#lQz$un3yZ;EMts?y1NWJegf!Yd!Zv z?rYpGKesEl3AZMY>{;yb?1Ahq?56CR>~idU>7# z>RC!zGFakR0$7|_3|TZ-WLfxFn3#VszhS=1e4hCT^H%2N%(IyLnQNJgm{Y+Q``I!Z zF{?65GIKHgWqQSQ7d&jRi>ZDc(^96{O#Mu)OjS&|ObJZEpzbypb2TY4Ge{Sg6eSiH zr*btZFf&N!7iFd==4Ns=$ul!ZCl(iEB<7TGG|4eDh!^A+<>j+C$ucvD=A{;wa5l*> zGf0A1IjQVT(##B^xrxQaY)w+k48kR;shmxc%nXv5c`2Fosd=1D63h&eNr{!IMR}Y} z;>-+^xuwN9nYkQIV$2NUNu@>EiCj&h%nZ^6r70G`Rk4T8l5shJ?x z2{SWDrzPfQ=47VwGzl>?$R`#hXJnS7W|wd_2{JQCrlb~Ursr`r34k09Ii7-}iJzIF zUc4Bz41}+VkC{O^AH0_jd}07+6E8D^B60Rn0a4>*Q?#}0E z;$mhH2cN;q-^9txpqgHq3EohiRGOT@(Zm4?mdwPwe6A*TW(Mi(9Pk0f+)ZrE46;?= zJ^iVfJoQbi%nb4=iJ&7(N;7genpi-(5{pX`xto}o8Dx_(^YcnllQVdlm_X`4dy_#+ zM>v}pnHeNYGC`Yi*_#-^p@Rm=>spi3`6CkAsjRx&flmK2qifDU$HZ>(Tu z5G_fp$mD7)XJ(MD$}B0$&r4-*EMsO6O;629Vs9*EW)MwE%**CzEMaC4PcBN$P33GX z1|CpPVF!5> zaWrN!Gl=KrrP)0oE0AYYbR1m5aTr2J!Tw($qYz#zbZY>Gag3#MI<$ zj>ZI#!_pG-QrH{gA)%AP(HIAEMQLhEDo0~1C_RDBc;KsVjA3R_PDC8Sz}Xnh%pjST zlbD{K%GDSJ%7Kt`7PuNCK^)ML2&s9TjS(Pch^8dw=5jRpgHjM^mpx~rAISDn&`KqaMqg$I@kG#meU3&SP<{aI z=jUwnW@eCt1Se;s7c+xo7I?osN24b*gLrX4eo+ZeqX#pCe0>3I+dN03J4mV|u{e{v z(T$lw7P4_233Use9N23)e$Us|i*&8iEi8wVUm#5K!nL!>L`JgSeoQ>wp43b6Q z4Yyp4W}u`Ax>6}MldI7bRQ^EjOyX`dVP>e81uf1@EvZW7Z8TQeKzBLiaWo2n@^WTQat3E3sCLN#U#h{;C;-aWpj#U_8u^(S#LGa{1bZVND3v5; zgc?H25fJj@LBqUlA6rCg1mY6W`51a~7BCH*?QGQ8wVk+de1dc{f z#Zm%VmdM-4%*>#eUX+Co^;gV6OvvthAV|7F3}m7V*|FGbn=UGRU<6T-Bfg3v^6>Y7u7@C@vF0*A;P8 zf@*=plF}lM3g&tSaZtKWV=ZT95KJxQE@Nhp1)YKq@;W=HKFLkY%i}Hq1p)MId-h^b zYa%tZh`k6DIr;e|yoI3h4}R!8PXRN7Jova9P>$!yXJ(L2Ni8iYPR`)W0~d^-TAm{p zTn^;cr{-|xfJ8x!oFe{gW(L*V)YPJ^(!BJvqQp{;EKuS}%trEQ*$`dKnboWu{4h(6_hqWB@R~#sI8D(l3!3-%##eNzKfGHKnI!R zaDXa~-2C*MOc(Y zPy;DHH7PZRJ(`(8G&4Ijaq}x_DHaPUzP$|m-}qnh-{rr^f0Tb0|9bF3&}RNh{w)4D z{s4YAeoKA>er0}9eh$9Bd>{E9^IhgU$+vg2qCz8IJ!g|OsNP8h-6O!>WChBDc_pd5 zO_t0IifKiOdD-A*5qFaXGlMLs;g*<}TEy7|D(4al5|cAhIhxEsAqr}gaW#QTx%3?H z=_%YzCd>@7S*7KvIhm-dxok~Fpu)T$k*mqD9$emn%Xs!CP}x?T znq9)(qz{VtN zQ<~1%qy~y%(7h$8oK2u2E*E?j1!t2A$bN8}i>pZ)l(q{pQ*%=D*qf9z^N zvA<&AzsG-we-VEte;U6FzZ~B`zB_#T_-66d^QG}Q^C|HD=e@^q5wYO%{32vv4soh_K_se z)`OH%@HsfCMLC(EGxCZ-ci$8g0H3fesZxgrL1yH09Rssi|c4!^`A z&>0(vrN!x~>Gh!N_do~BC}>nBmS-oHXnN&mB!U{{@k%{;kUaq5paMEOzaXtRHMJ-) zB?oyMgIrH8WX*k0eo1*M=*BnDorOrty=8iGAR83C6LSmFior*)6(JUK*Gu$dL-q+o zB^IQCn#~9~`JODuRu3moyQuW={F1}latczB_LwO4WU?_pj*ZGZJRf9#acU}>3&ndf z*cd=JdwC?LWF}`I?FUilNoT8P03BfHmYM>(4lwiZ{Nj@QqT~#O8)SOYAkGIJ-j$e_ z4LTzbX-9!nPby^Zhj)HaUQQ}#DLO*0LQe`C*m&>5+wwBgiwiPyit|8S9)uFP`krJ+ z_yuPsXO!jVP%^C)>q&&{X#kxumyZ-S zN<9gX=yNPe&PYs6%*oC#$S){KEvl?Q*soOI6Aw}4l3!4env7I!ejxs%w&}b?t%FnPcD9=E?MTfU#Av%!f7bix?I zL$W=gkT?PzGFO_MU0eX_njz$+dO{$n1S(&O*dbJ(TB+6(%*FsZInF0PFI~YC+=v04 zu2=#(0}45>N%jOmg2uflv7jI^Jv9?yg>+9K#0SA8shN4`xm2rYCS#>Z#kt_C1&KL zq~^J#CTFMSlpo%Mv=Iz+jigSGHyZ=!+RA{^qJq*QNDk0Q&rB*Q1{G}Cpdv~WVW)hL z7o=El1fAlPe|SE|)-#nJPc{Zq&<&u6_kd3H%mY>apt=uqp%20!r5+EqdhjkM_ae|q zdqoOPprd8MjYWhixgK{&?1K&+0%h|OQ0OBiE`=U9NbUeBaLudAOw0ovmRg0P#1)|= zB)=#-H3zDs9&|GrG$Xq}GI9`F2_Vtq3<+aU4$mmfKq_1ndz>KUUoa?Tf=+S-9kPa+ zIHh|WAt?iT^K52P5mLITm+x_agr-wsQ3j|v0lKNJJQ2wf*&cgH`Up!cDlW|}NJB|L zsg=q-c5DpB3>=E!BC@C|3Dmv8h!go9TZld2+61(<4P4aLBbBuZJvNX~0;SS4P=( z?(MInS)^FF*!Z_H@PFlh!~bA2pTaEui2=OHf^5v{jK%pyC8taCtibMP-?;Qz+| zmj6EgHU8852l+ShFWW9yz|_Y-d6|72hctfz0|Ntt#bh1_v(1VMiu{w4{TIq`@gD(+ zTJrzm|HA){{{{ae{yY5F_%HCE;y(h?yqv%JczpZuc*gC=L9S$;ia2pv<+7fq}u4o0W-?k%?sk3ol3F_C@!Z;uF{z7#LNVf*Ga<1Tdf8 ze%XP!fpNQB2=ffa?XR7gIeA3jI#@XpxcI*^YA_`;ZVw1x-oseWx{7r^>txn;)@s&# z)@0TQR$o>pRx?&@Rs~iOR!)|GET37Pv)p01$a0ir7t1=9#VpfVx>@R3idiyPVp)P% z+*zzy3|LfHBw2V_n3%sYzh=JAe1-W0^IqnS%*&W(G50e!GgmO@FefsHGJ7)HG8-|g zF-tM?F*7lJV|vAOpGn~g(@CbiOdFY&GRE>yO(z(?^52GyuG}QyrsOE zy!COsA-tZv_PnONn!K{Sg1l@ze|SFfJmbN}Xk&;6MDI`?Vr{oI?mXLB#-?&ogiuHeq$PUQCFw&f1xHsV&} zmg45;`p(VF^_uHG*JZBbT)Vl}b1mkY&ehFTT+da{mChB-<o3o#@nX{ZTn=_s>nA4rpnp2-snNysTo8v#nXO8C_w>i#p z9Ol^0v6^E($7GInj%tp4j%1E-4sQ;74r2~=4rva44rcc6?62AHvtMRE&c2&{J^Nzz z>FnL?_4Vw$?Aq+k?EdWL>T z<+CNTg|m6H*|Qn5sk2G5@v||rerJ8n`hfK+>nYYPtovDWSSwgpu+Cwfz}muU!|K7B zz#76T!K%V)z{a}2W!a{#l&#zqcC6-bI=ox>8v zeHhMW<~{^xvvMDteqb)MC_gjzeyHGV7FO=P`TPvvMzo%1mML z=3WM6PiEodUJ7S>aW8?gC$U&^FNU*Oxfe~Bna?aQJCVhmdp=ZnA`2_`JUH8ud+zjt z`OKomtlYDqf)iLQxo5%IZrn4W?0yzj?ip~lCHM5{3+6M+3-+fCuy z^BP&qxO3rbE$*D@ACj3xw;xMomSCR#DubDeHI+q%TYdW54CYdPer{Q)(ij#QZkg$Y znat+WLfqm|!3Y*kZZSAplv{NA;Y?;{er9fds9-P)D>vVE(JW?P7IsjzAj`6EI(q@L l^~S~;mhJmWm|eM97!>(}r?X#Rbl$9}z{$5gxQ}@)BLL-cqE`R_ diff --git a/boddle.go b/boddle.go index 1e78497..c1bc72c 100644 --- a/boddle.go +++ b/boddle.go @@ -308,6 +308,10 @@ func parsemsg(msg *irc_msg, foo bot) bool { if len((*msg).msg) <= 0 { return false } + if (*msg).msg == "<3" { + (*msg).msg = string(begin_char[0]) + "flirt kreisi" + } + if !in((*msg).msg[0], begin_char) { return false } From f6cf8b46ecb08d577375debffcf31616c6dc09e8 Mon Sep 17 00:00:00 2001 From: Eva Dengler Date: Sat, 5 Mar 2022 21:39:54 +0100 Subject: [PATCH 04/10] kill bot after 10 minutes idling should prevent 'ping loss' due to weird network setup --- boddle.go | 71 ++++------------------------- bot.go | 118 +++++++++++++++++++++++++++++++++++++++---------- forraspidothis | 2 +- helpers.go | 45 +++++++++++++++++++ ircfoo.go | 99 ----------------------------------------- logging.go | 8 ++-- pepe.go | 40 ----------------- types.go | 34 ++++++++++++++ 8 files changed, 187 insertions(+), 230 deletions(-) create mode 100644 helpers.go delete mode 100644 ircfoo.go delete mode 100644 pepe.go create mode 100644 types.go diff --git a/boddle.go b/boddle.go index c1bc72c..50a6807 100644 --- a/boddle.go +++ b/boddle.go @@ -1,70 +1,17 @@ package main -import "strings" -import "fmt" -import "regexp" -import "database/sql" -import _ "github.com/mattn/go-sqlite3" - -type irc_msg struct { - channel string - msg string - author string - retmsg string -} - -type cmd struct { - valid bool - add bool - cmd string - groups []string - suffix string - error string -} +import ( + "strings" + "fmt" + "regexp" + "database/sql" + _ "github.com/mattn/go-sqlite3" +) var begin_char = []byte{'-', '<'} var db *sql.DB var err error -func checkErr(err error) { - if err != nil { - fmt.Printf("::::ERROR:::: %s\n", err) - panic(err) - } -} - -func in (a byte, arr []byte) bool { - for _, x := range arr { - if x == a { - return true - } - } - return false -} - -func unique_str(stringSlice []string) []string { - keys := make(map[string]bool) - list := []string{} - for _, entry := range stringSlice { - if _, value := keys[entry]; !value { - keys[entry] = true - list = append(list, entry) - } - } - return list -} - -func unique_int(intSlice []int) []int { - keys := make(map[int]bool) - list := []int{} - for _, entry := range intSlice { - if _, value := keys[entry]; !value { - keys[entry] = true - list = append(list, entry) - } - } - return list -} // function to split advise message correctly func filter(msg string, foo bot) cmd { @@ -232,7 +179,7 @@ func checkSorter(msg *irc_msg, c *cmd) bool { sorter_groups_row.Close() if (*c).add { var tag_name string - tag_name_row, err := db.Query(fmt.Sprintf("select name from tag where id = %d", tag_id)) + tag_name_row, err := db.Query(fmt.Sprintf("select name from tag where id = %s", tag_id)) checkErr(err) if tag_name_row.Next() { tag_name_row.Scan(&tag_name) @@ -281,7 +228,7 @@ func addLine(msg *irc_msg, line string, groups []string, cmd string) string { line_id, err := res.LastInsertId(); checkErr(err) - LOG_WARN.Printf("line_id: %d, tag_id: %d, groups: %s\n", line_id, cmd, strings.Join(groups, "-")) + LOG_WARN.Printf("line_id: %d, tag_id: %s, groups: %s\n", line_id, cmd, strings.Join(groups, "-")) // for tag and all groups if len(groups) == 0 { stat, err := db.Prepare("insert into brain(line_id, tag_id) values (?,?)") diff --git a/bot.go b/bot.go index afc9c04..9fd056e 100644 --- a/bot.go +++ b/bot.go @@ -6,22 +6,94 @@ import ( "fmt" "net" "time" + "bufio" + "bytes" + "strings" + "database/sql" configo "github.com/distributedio/configo" _ "github.com/mattn/go-sqlite3" ) -var dead = true - -type Config struct { - Name string `cfg:"name; boddle; printableascii; IRC nick of bot"` - Channels []string `cfg:"channels; required; printableascii; Channel list to join to"` - Server string `cfg:"server; required; netaddr; Server name to connect to"` - Database string `cfg:"database; ./boddle.db; path; Path to database"` +func sendmsg (conn net.Conn, channel string, msg string) { + mesg := fmt.Sprintf("PRIVMSG %s :%s\r\n", channel, msg) + fmt.Printf(mesg) + conn.Write([]byte(mesg)) } -type bot struct { - Conf Config - conn net.Conn +func scanline_privmsg (msg string) irc_msg { + var irc irc_msg + + msg = msg[1:] + t := strings.Split(msg, "!") + irc.author, irc.channel = t[0], t[1] + t = strings.Split(irc.channel, "PRIVMSG ") + irc.channel = t[1] + t = strings.Split(irc.channel, " :") + irc.channel, irc.msg = t[0], t[1] + t = strings.Split(irc.msg, "\r\n") + irc.msg = strings.TrimSpace(t[0]) + if ! strings.HasPrefix(irc.channel, "#") { + irc.channel = irc.author + } + irc.retmsg = "" + return irc +} + +func listenToIRC (foo bot) <-chan []byte { + c := make(chan []byte) + reader := bufio.NewReader(foo.conn) + db, err = sql.Open("sqlite3", foo.Conf.Database) + if err != nil { + LOG_ERR.Printf("opening database failed") + return nil + } + + go func() { + for { + line, err := reader.ReadString('\n') + if err != nil { + LOG_ERR.Printf("Error while reading bytes from connection.\n") + close(c) + return + } + + c <- []byte(line) + + if bytes.Contains([]byte(line), []byte("PING :")) { + v := []byte(bytes.Replace([]byte(line), []byte("PING"), []byte("PONG"), 1)) + fmt.Printf(string(v[:])) + foo.conn.Write(v) + } else if bytes.Contains([]byte(line), []byte("PRIVMSG")) { + msg := scanline_privmsg(string(line)) + parsemsg(&msg, foo) + if msg.retmsg != "" { + sendmsg(foo.conn, msg.channel, msg.retmsg) + } + } + } + } () + return c +} + +func connect(boddle *bot) <-chan []byte { + conn, err := net.Dial("tcp", (*boddle).Conf.Server) + + if err != nil { + fmt.Fprintf(os.Stderr, "Error while dialing server.") + LOG_ERR.Printf("Error while connecting to server.") + return nil + } + + (*boddle).conn = conn + + /* connect to irc */ + fmt.Fprintf((*boddle).conn, "NICK %s\r\n", (*boddle).Conf.Name) + fmt.Fprintf((*boddle).conn, "USER %s 1 1 1:%s\r\n", (*boddle).Conf.Name, (*boddle).Conf.Name) + for _, channel := range((*boddle).Conf.Channels) { + fmt.Fprintf((*boddle).conn, "JOIN %s\r\n", channel) + } + + return listenToIRC((*boddle)) } func main() { @@ -35,23 +107,21 @@ func main() { LOG_INFO.Println("bot started!") var botchan <-chan []byte - var line []byte - dead = true for { - if dead { - LOG_WARN.Println("died.") - for { - botchan = connect(boddle) - if botchan != nil { - break - } - time.Sleep(10 * time.Second) + botchan = connect(&boddle) + for { + select { + case res := <-botchan: + fmt.Println(string(res)) + case <-time.After(10 * time.Minute): + fmt.Println("timeout - 10 Minutes no ping received.") + // close channel, close connection + boddle.conn.Close() + goto Reconnect } - LOG_INFO.Println("reconnected.") - dead = false } - line = <-botchan - fmt.Fprintf(os.Stderr, string(line)) +Reconnect: + fmt.Println("trying to reconnect...") } } diff --git a/forraspidothis b/forraspidothis index 1bc0a64..d295ea6 100755 --- a/forraspidothis +++ b/forraspidothis @@ -9,7 +9,7 @@ export GOARCH=arm export GOARM=7 declare -a src -src+=(bot.go boddle.go logging.go ircfoo.go) +src+=(bot.go boddle.go logging.go helpers.go types.go) declare -i nproc="$(nproc)" go build -p ${nproc} "${src[@]}" diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..f59c740 --- /dev/null +++ b/helpers.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" +) + +func checkErr(err error) { + if err != nil { + fmt.Printf("::::ERROR:::: %s\n", err) + panic(err) + } +} + +func in (a byte, arr []byte) bool { + for _, x := range arr { + if x == a { + return true + } + } + return false +} + +func unique_str(stringSlice []string) []string { + keys := make(map[string]bool) + list := []string{} + for _, entry := range stringSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func unique_int(intSlice []int) []int { + keys := make(map[int]bool) + list := []int{} + for _, entry := range intSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} diff --git a/ircfoo.go b/ircfoo.go deleted file mode 100644 index cb04aac..0000000 --- a/ircfoo.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "fmt" - "net" - "os" - "bufio" - "bytes" - "strings" - "database/sql" - _ "github.com/mattn/go-sqlite3" -) - -func sendmsg (conn net.Conn, channel string, msg string) { - mesg := fmt.Sprintf("PRIVMSG %s :%s\r\n", channel, msg) - fmt.Printf(mesg) - conn.Write([]byte(mesg)) -} - -func scanline_privmsg (msg string) irc_msg { - var irc irc_msg - - msg = msg[1:] - t := strings.Split(msg, "!") - irc.author, irc.channel = t[0], t[1] - t = strings.Split(irc.channel, "PRIVMSG ") - irc.channel = t[1] - t = strings.Split(irc.channel, " :") - irc.channel, irc.msg = t[0], t[1] - t = strings.Split(irc.msg, "\r\n") - irc.msg = strings.TrimSpace(t[0]) - if ! strings.HasPrefix(irc.channel, "#") { - irc.channel = irc.author - } - irc.retmsg = "" - return irc -} - -func listenToIRC (foo bot) <-chan []byte { - c := make(chan []byte) - reader := bufio.NewReader(foo.conn) - db, err = sql.Open("sqlite3", foo.Conf.Database) - if err != nil { - LOG_ERR.Printf("opening database failed") - return nil - } - - go func() { - for { - line, err := reader.ReadString('\n') - if err != nil { - LOG_ERR.Printf("Error while reading bytes from connection.\n") - dead = true - c <- []byte("killed it") - return - } - - c <- []byte(line) - - if bytes.Contains([]byte(line), []byte("PING :")) { - v := []byte(bytes.Replace([]byte(line), []byte("PING"), []byte("PONG"), 1)) - fmt.Printf(string(v[:])) - foo.conn.Write(v) - continue - } - - if !bytes.Contains([]byte(line), []byte("PRIVMSG")) { - continue - } - msg := scanline_privmsg(string(line)) - parsemsg(&msg, foo) - if msg.retmsg != "" { - sendmsg(foo.conn, msg.channel, msg.retmsg) - } - } - } () - return c -} - -func connect(boddle bot) <-chan []byte { - conn, err := net.Dial("tcp", boddle.Conf.Server) - - if err != nil { - fmt.Fprintf(os.Stderr, "Error while dialing server.") - LOG_ERR.Printf("Error while connecting to server.") - return nil - } - - boddle.conn = conn - - /* connect to irc */ - fmt.Fprintf(boddle.conn, "NICK %s\r\n", boddle.Conf.Name) - fmt.Fprintf(boddle.conn, "USER %s 1 1 1:%s\r\n", boddle.Conf.Name, boddle.Conf.Name) - for _, channel := range(boddle.Conf.Channels) { - fmt.Fprintf(boddle.conn, "JOIN %s\r\n", channel) - } - - return listenToIRC(boddle) -} diff --git a/logging.go b/logging.go index 02769c6..6c03744 100644 --- a/logging.go +++ b/logging.go @@ -12,9 +12,9 @@ var ( ) func LOG_init() { - file := os.Stdout + file := os.Stdout - LOG_INFO = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - LOG_WARN = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) - LOG_ERR = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + LOG_INFO = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + LOG_WARN = log.New(file, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) + LOG_ERR = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) } diff --git a/pepe.go b/pepe.go deleted file mode 100644 index 9ba9fd5..0000000 --- a/pepe.go +++ /dev/null @@ -1,40 +0,0 @@ -package main - -var pepe = []string{ -"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░░░░░░░░░▓▓▒▒▒▒▒▒▓▓▒░░░░░░░░░▓▓░▓▓▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░░░░░ ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░▓▒▒▒▒▒▒▒▒▒▒▒▒▓░░ ░░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓░░░░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░░░▓▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▓▓▓▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓ ░░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓░░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓░▓▓▒▒▓▓▓░░▒▒▒▒▒▒▓▓░░░░░▓▓▒▓░░░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░░░░▓▓▒▒▒▒▒▒▒▒▒▒▒▓▓▒▓▓▓▒▒▒▒▒▒▒▒▓▓▓░▓▓▓▓▓▓▒▒▒▒▒▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░░▒▒▒▓▒▒▒▒▒▒▒▒▒▓▒▓▓▓▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▓▒▒▒▒▓▓▓▒▒▓▓▓▒▒▒▒▓▓░░░░░░░░░░░░░░░░░░", -"░░░░░░░░░▓▒▒▒▓▒▒▒▒▒▒▒▓▓▒▒▒▒▒▒▓▓▒░▒█░███░ ▒ ░█▓░████ ░░░░░░░░░░░░░░░░░░", -"░░░░░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ ░████░ ██░ ▒ ██░██ ░██ ▒░░░░░░░░░░░░░░░░░░", -"░░░░░░ ▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▓▓▒███░███░▓▒▒░▒▒▓▓▓▓░░░▓▓▓▒▒▒▓▒░░░░░░░░░░░░░░░░░░░", -"░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒░░░░░░░░░░░░░░░░░░░░░", -"░░░░░▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▓▓▓▒▒▒▒▓▓░▒ ░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░░░░░░░░░░░░░░░░░░░░░░░", -"░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░░░░░░░░░█░░▒▓█▓░█░░░░", -"░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓ ░░░░░░░█▒░█▒▒░▓▒█▒░░", -"░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░▓▒▒▒▓▓░▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓░▓░▒░░▓░░▓▓▒░░▒▓█▓▒█░░", -"░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▓░░░░▓▓▓▓▓░░░▓▒▒▒░▒▒░░▒░▓█ ░█▒▒░░▒░▒▓▓░░", -"░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▓▓▓▓░░░░░▓▓▓█▒█▒▒▒▒▒▒▒█░░░", -"░░░░░▒▓▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▓█░█░▓▒█▓░▒▒▒▒▒▒▒▒▒▓ ░░░░░░▒▒▒▒▒▒▒▒▒▒█░░░░", -"░░░░░░░▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒█▓▓░▒█░█▓▒█░░░▓▓▓▓▒░░░░░▒▒▒▒▒▒▒▒▒▒▒▓█▒░░░░░", -" ░ ░▒▒ ▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓██▒█▒▒░▒▓▓▒█▒▓░▓░░▒ ░░░░▒░▒▒▒▒▒▒▒▒▒▒▓█░░░░░░░░░", -" ▒ ░▓▓▒▓▓░▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒█▒█░▒▒▓▒▒█░▓▒▓░█▒▒░░░░░ ░▒▒▒▒▒▒▒▒▓█▒░░░░░░░░░░░", -"▒ ░▓▓▓▒▒▒▒▒▒▓▓▓▓▓▓▓▓░▓▒░▒▒▒▒▒▒▒▒▒░▒▒▒▓░░░░░░░▒▒▒▒▒▒▒▒░ ░░░░░░░░░░░░", -" ░░░▒▒▒▒▒▓█▓▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░▒▓▒▒▒▒▒▒▒░ ░░░░░░░░░░░░", -" ░▒▒▒▒░▓▒▒█▒ ▒░░░░░░░░░█▒▒▒▒▒▒▒▓▒░░░░░░░░░░░░", -" ▓▒▒▒▒▒▒▒▓░ █▒░░░░░▒▒▒▒▒▒▒▒▒█░░░░░░░░░░░░", -" ░▓▒▒▒▒▒▒▒░ ░░░░░░░▓▒▒▒▒▒▒▒▓▓░░░░░░░░░░░"}; - - -func printPepe(channel string, foo bot) { - for _, x := range pepe { - sendmsg(foo.conn, channel, x) - } - LOG_INFO.Printf("Sent pepe to ppl") -} diff --git a/types.go b/types.go new file mode 100644 index 0000000..b2968c4 --- /dev/null +++ b/types.go @@ -0,0 +1,34 @@ +package main + +import ( + "net" +) + +type Config struct { + Name string `cfg:"name; boddle; printableascii; IRC nick of bot"` + Channels []string `cfg:"channels; required; printableascii; Channel list to join to"` + Server string `cfg:"server; required; netaddr; Server name to connect to"` + Database string `cfg:"database; ./boddle.db; path; Path to database"` +} + +type irc_msg struct { + channel string + msg string + author string + retmsg string +} + +type cmd struct { + valid bool + add bool + cmd string + groups []string + suffix string + error string +} + +type bot struct { + Conf Config + conn net.Conn +} + From e45fd544b53249c0ca43b28b5749b34a17ef816c Mon Sep 17 00:00:00 2001 From: Eva Dengler Date: Sun, 6 Mar 2022 22:48:28 +0100 Subject: [PATCH 05/10] update timeout --- bot.go | 148 +++++++++++++++++++++++++++++++++++---------------------- go.mod | 13 +++++ go.sum | 18 +++++++ 3 files changed, 122 insertions(+), 57 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/bot.go b/bot.go index 9fd056e..c822409 100644 --- a/bot.go +++ b/bot.go @@ -1,13 +1,11 @@ package main import ( - "os" "log" "fmt" "net" "time" "bufio" - "bytes" "strings" "database/sql" configo "github.com/distributedio/configo" @@ -39,61 +37,116 @@ func scanline_privmsg (msg string) irc_msg { return irc } -func listenToIRC (foo bot) <-chan []byte { - c := make(chan []byte) - reader := bufio.NewReader(foo.conn) - db, err = sql.Open("sqlite3", foo.Conf.Database) +func bot_connect(bot *bot) { + conn, err := net.Dial("tcp", (*bot).Conf.Server) if err != nil { - LOG_ERR.Printf("opening database failed") - return nil + LOG_ERR.Printf("Error while dialing server.") + (*bot).conn = nil + return } - go func() { + (*bot).conn = conn + fmt.Fprintf((*bot).conn, "USER %s 1 1 1:%s\r\n", (*bot).Conf.Name, (*bot).Conf.Name) +} + +func bot_register(bot *bot) { + /* connect to irc */ + fmt.Fprintf((*bot).conn, "NICK %s\r\n", (*bot).Conf.Name) + for _, channel := range((*bot).Conf.Channels) { + fmt.Fprintf((*bot).conn, "JOIN %s\r\n", channel) + } +} + +func bot_listen(bot *bot) <-chan string { + c := make(chan string) + bot_register(bot) + + go func(timeoutNormal time.Duration, timeoutVersion time.Duration) { + bufReader := bufio.NewReader((*bot).conn) + + timeoutDuration := timeoutNormal + waitversion := false + + defer func() { + fmt.Println("Closing connection...") + close(c) + (*bot).conn.Close() + }() + for { - line, err := reader.ReadString('\n') + (*bot).conn.SetReadDeadline(time.Now().Add(timeoutDuration)) + + line, err := bufReader.ReadString('\n') if err != nil { - LOG_ERR.Printf("Error while reading bytes from connection.\n") - close(c) - return + if nerr, ok := err.(net.Error); ok && nerr.Timeout() { + fmt.Printf("Timeout!\n") + if waitversion { + fmt.Println("VERSION command did not return.") + return + } + + n, err := (*bot).conn.Write([]byte("VERSION\n")) + fmt.Printf("n: %d, err: %s\n", n, err) + if err != nil { + fmt.Println("Writing to channel failed.") + return + } + // wait for return value of VERSION + waitversion = true + timeoutDuration = timeoutVersion + } else { + fmt.Println("Some more serious error occured while reading.") + return + } + continue } - c <- []byte(line) + // not waiting for version answer right now + timeoutDuration = timeoutNormal + waitversion = false + c <- line - if bytes.Contains([]byte(line), []byte("PING :")) { - v := []byte(bytes.Replace([]byte(line), []byte("PING"), []byte("PONG"), 1)) - fmt.Printf(string(v[:])) - foo.conn.Write(v) - } else if bytes.Contains([]byte(line), []byte("PRIVMSG")) { - msg := scanline_privmsg(string(line)) - parsemsg(&msg, foo) + if strings.Contains(line, "PING") { + (*bot).conn.Write([]byte(strings.Replace(line, "PING", "PONG", 1))) + } else if strings.Contains(line, "You have not registered") { + // set nickname + time.Sleep(10 * time.Second) + bot_register(bot) + } else if strings.Contains(line, "PRIVMSG") { + msg := scanline_privmsg(line) + parsemsg(&msg, (*bot)) if msg.retmsg != "" { - sendmsg(foo.conn, msg.channel, msg.retmsg) + sendmsg((*bot).conn, msg.channel, msg.retmsg) } } } - } () + } (10 * time.Second, 5 * time.Second) + // } (4 * time.Minute, 1 * time.Minute) + return c } -func connect(boddle *bot) <-chan []byte { - conn, err := net.Dial("tcp", (*boddle).Conf.Server) +func bot_run(bot *bot) { + LOG_INFO.Println("Bot ready to run.") + LOG_INFO.Println("Opening database.") + db, err = sql.Open("sqlite3", (*bot).Conf.Database) if err != nil { - fmt.Fprintf(os.Stderr, "Error while dialing server.") - LOG_ERR.Printf("Error while connecting to server.") - return nil + LOG_ERR.Println("Opening database failed.") + return } - (*boddle).conn = conn - - /* connect to irc */ - fmt.Fprintf((*boddle).conn, "NICK %s\r\n", (*boddle).Conf.Name) - fmt.Fprintf((*boddle).conn, "USER %s 1 1 1:%s\r\n", (*boddle).Conf.Name, (*boddle).Conf.Name) - for _, channel := range((*boddle).Conf.Channels) { - fmt.Fprintf((*boddle).conn, "JOIN %s\r\n", channel) + for { + bot_connect(bot) + if bot.conn == nil { + LOG_INFO.Printf("Bot is nil. Try to connect again.") + time.Sleep(10 * time.Second) + continue + } + for val := range bot_listen(bot) { + LOG_INFO.Printf("%s", val) + } } - - return listenToIRC((*boddle)) } func main() { @@ -104,24 +157,5 @@ func main() { log.Fatal(err) } - LOG_INFO.Println("bot started!") - - var botchan <-chan []byte - - for { - botchan = connect(&boddle) - for { - select { - case res := <-botchan: - fmt.Println(string(res)) - case <-time.After(10 * time.Minute): - fmt.Println("timeout - 10 Minutes no ping received.") - // close channel, close connection - boddle.conn.Close() - goto Reconnect - } - } -Reconnect: - fmt.Println("trying to reconnect...") - } + bot_run(&boddle) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ef1cee4 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module boddle + +go 1.17 + +require ( + github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816 + github.com/mattn/go-sqlite3 v1.14.12 +) + +require ( + github.com/naoina/go-stringutil v0.1.0 // indirect + github.com/shafreeck/toml v0.0.0-20190326060449-44ad86712acc // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..eb5290c --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816 h1:V6TyTUY0vUVUOxLnuH6AIOCIOI8psy5h0n4U+qvO4bo= +github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816/go.mod h1:Jwz2omP6W/T/XlSfu+BMGW7NEJX3tf5/Qv5gwaiQ+uU= +github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= +github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/shafreeck/toml v0.0.0-20190326060449-44ad86712acc h1:BrtrZvICmDsYzv7ECoQFwlC5cS+YWDfz/OBpMlMe9HY= +github.com/shafreeck/toml v0.0.0-20190326060449-44ad86712acc/go.mod h1:C9DYu7Ddz1xnXil/kyvydcdaUggQeJvFA7vzYpm+Cw4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190318195719-6c81ef8f67ca/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= From 7683f9ee032b9f08f86040d36aa0a2204db01600 Mon Sep 17 00:00:00 2001 From: Horscchtey Date: Tue, 19 Apr 2022 19:22:56 +0000 Subject: [PATCH 06/10] add correct time --- bot.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bot.go b/bot.go index c822409..3f68137 100644 --- a/bot.go +++ b/bot.go @@ -120,8 +120,7 @@ func bot_listen(bot *bot) <-chan string { } } } - } (10 * time.Second, 5 * time.Second) - // } (4 * time.Minute, 1 * time.Minute) + } (4 * time.Minute, 1 * time.Minute) return c } From e5a935a85b997fe3ae9aae5ca17d75d191d97278 Mon Sep 17 00:00:00 2001 From: Horscchtey Date: Wed, 20 Apr 2022 10:41:24 +0000 Subject: [PATCH 07/10] backup database file --- boddle.db | Bin 278528 -> 290816 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/boddle.db b/boddle.db index 89fa358e86b32c4557233283d28e05bb5806c097..31db554b75c27876bd1399b556269784797688dc 100644 GIT binary patch delta 7227 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 z Date: Wed, 21 Dec 2022 13:04:36 +0100 Subject: [PATCH 08/10] Update repository --- boddle.db | Bin 290816 -> 290816 bytes boddle.go | 15 ++++++++++++++- bot.go | 29 +++++++++++++++-------------- bot.py | 2 +- forraspidothis | 9 ++------- go.mod | 2 +- go.sum | 2 ++ 7 files changed, 35 insertions(+), 24 deletions(-) diff --git a/boddle.db b/boddle.db index 31db554b75c27876bd1399b556269784797688dc..c6dd3eac1594c9d0700b0c6e510f2480d15f3c88 100644 GIT binary patch delta 6695 zcmZp8AlUFgaDp`B_lYvjtlt^*q762t1n4ssPu{Ap#n?LefxbNBf+mAD1IBFzOl%9} zoEaDx7~J?j@;~Cg#D9o?6aOOqN&LS{8`33V`=1a`SnfEYnWM0NRn|T6r zE6-A%Sv>tbEj*PxxjczXkv!WanOJfdr{~48Y@cq!!eYtBCd0zYXT80eg=HrjD+4KDORpJ%pW##$F^Ool!&vl;DJbQUI@+{+-#nZ=A z&6CfQ$`i)p&Ev>p%%jO8&m+27Q6ZDNp1rAEy`tYDr9C5%}**W z;chBmW{@o@O3lkF%}Zx*%4cQ}%}Fdx;%LfaW)M$HEJ{k{Y0710kS{JNO3h5pNX=t! z%3)>@EiTE-;cUugW{@mS&PXk)5@^a|W>C-0FGwp+&d5zIs!Gk{Y|3P2kgQM6NX^Ym zDE9^NLfmOSqd-nHgk@lQS|?(^88#n^Kq= zB�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 diff --git a/boddle.go b/boddle.go index 50a6807..11c733a 100644 --- a/boddle.go +++ b/boddle.go @@ -5,10 +5,11 @@ import ( "fmt" "regexp" "database/sql" + "math/rand" _ "github.com/mattn/go-sqlite3" ) -var begin_char = []byte{'-', '<'} +var begin_char = []byte{'-'} var db *sql.DB var err error @@ -278,6 +279,18 @@ func parsemsg(msg *irc_msg, foo bot) bool { return false } if c.cmd == "help" { + (*msg).retmsg = "There is no help in this hell..." + return false + } + if c.cmd == "slap" { + who := []string{(*msg).author, "\x01ACTION"} + whom := []string{(*msg).author, foo.Conf.Name} + LOG_WARN.Printf("suffix: " + c.suffix) + if c.suffix != "" { + who = append(who, strings.Split(c.suffix, " ")...) + whom = append(whom, strings.Split(c.suffix, " ")...) + } + (*msg).retmsg = who[rand.Intn(len(who))] + " slaps " + whom[rand.Intn(len(whom))] + " around with a large trout." return false } // hard code 'groups' to add new groups to the groups-table diff --git a/bot.go b/bot.go index 3f68137..c2598df 100644 --- a/bot.go +++ b/bot.go @@ -18,23 +18,22 @@ func sendmsg (conn net.Conn, channel string, msg string) { conn.Write([]byte(mesg)) } -func scanline_privmsg (msg string) irc_msg { - var irc irc_msg +func scanline_privmsg (msg string, irc *irc_msg) { + LOG_ERR.Printf(msg) msg = msg[1:] t := strings.Split(msg, "!") - irc.author, irc.channel = t[0], t[1] - t = strings.Split(irc.channel, "PRIVMSG ") - irc.channel = t[1] - t = strings.Split(irc.channel, " :") - irc.channel, irc.msg = t[0], t[1] - t = strings.Split(irc.msg, "\r\n") - irc.msg = strings.TrimSpace(t[0]) - if ! strings.HasPrefix(irc.channel, "#") { - irc.channel = irc.author + (*irc).author, (*irc).channel = t[0], t[1] + t = strings.Split((*irc).channel, "PRIVMSG ") + (*irc).channel = t[1] + t = strings.Split((*irc).channel, " :") + (*irc).channel, (*irc).msg = t[0], t[1] + t = strings.Split((*irc).msg, "\r\n") + (*irc).msg = strings.TrimSpace(t[0]) + if ! strings.HasPrefix((*irc).channel, "#") { + (*irc).channel = (*irc).author } - irc.retmsg = "" - return irc + (*irc).retmsg = "" } func bot_connect(bot *bot) { @@ -113,7 +112,9 @@ func bot_listen(bot *bot) <-chan string { time.Sleep(10 * time.Second) bot_register(bot) } else if strings.Contains(line, "PRIVMSG") { - msg := scanline_privmsg(line) + var msg irc_msg + scanline_privmsg(line, &msg) + LOG_ERR.Printf(msg.author) parsemsg(&msg, (*bot)) if msg.retmsg != "" { sendmsg((*bot).conn, msg.channel, msg.retmsg) diff --git a/bot.py b/bot.py index d623236..21ecae9 100644 --- a/bot.py +++ b/bot.py @@ -12,7 +12,7 @@ import sqlite3 as sql updater = Updater(token='935673062:AAH4By1EMqAUaD9wgnV3lZQRRBX6e5Lve6g', use_context=True) # sqlite database -connection = sql.connect("/home/horscchtey/bots/boddle/src/v2/boddle.db", check_same_thread=False) +connection = sql.connect("/home/boddle/boddle/src/v2/boddle.db", check_same_thread=False) cursor = connection.cursor() dispatcher = updater.dispatcher diff --git a/forraspidothis b/forraspidothis index d295ea6..6239be8 100755 --- a/forraspidothis +++ b/forraspidothis @@ -1,15 +1,10 @@ #!/bin/bash -export GOPATH=/home/horscchtey/go -export CC=arm-linux-gnueabihf-gcc -export CXX=arm-linux-gnueabihf-g++ +export GOPATH=/home/boddle/go export CGO_ENABLED=1 -export GOOS=linux -export GOARCH=arm -export GOARM=7 declare -a src src+=(bot.go boddle.go logging.go helpers.go types.go) declare -i nproc="$(nproc)" -go build -p ${nproc} "${src[@]}" +go build -v -p ${nproc} "${src[@]}" diff --git a/go.mod b/go.mod index ef1cee4..72f9306 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816 - github.com/mattn/go-sqlite3 v1.14.12 + github.com/mattn/go-sqlite3 v1.14.16 ) require ( diff --git a/go.sum b/go.sum index eb5290c..cb9d486 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816 h1:V6TyTUY0v github.com/distributedio/configo v0.0.0-20200107073829-efd79b027816/go.mod h1:Jwz2omP6W/T/XlSfu+BMGW7NEJX3tf5/Qv5gwaiQ+uU= github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.1 h1:PT/lllxVVN0gzzSqSlHEmP8MJB4MY2U7STGxiouV4X8= From 2e1890c9f92eed0ec3bce4687bc74b1dd9b909f6 Mon Sep 17 00:00:00 2001 From: Eva Dengler Date: Wed, 21 Dec 2022 13:38:43 +0100 Subject: [PATCH 09/10] add new functionality --- boddle.db | Bin 290816 -> 303104 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/boddle.db b/boddle.db index c6dd3eac1594c9d0700b0c6e510f2480d15f3c88..f87f1528fc108cfc4d5882ec746d2966d611a88b 100644 GIT binary patch delta 11019 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 delta 4221 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 wn(kf8@uV0L3kp^#A|> From 9b016251f25e66bcafc9891c139a4ded7d2aebc3 Mon Sep 17 00:00:00 2001 From: Jonas Rabenstein Date: Tue, 21 Jan 2025 16:52:47 +0100 Subject: [PATCH 10/10] setup nixos environment --- flake.nix | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 flake.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a59231c --- /dev/null +++ b/flake.nix @@ -0,0 +1,62 @@ +{ + description = "boddle, in Anlehnung an bottle"; + + outputs = { self, nixpkgs }: let + module = { pkgs, lib, config, ... }: { + options.services.boddle = { + enable = lib.mkEnableOption "enable"; + # TODO: configurability + }; + + config.nixpkgs.overlays = [ self.overlay ]; + + config.systemd.services.boddle = lib.mkIf config.services.boddle.enable { + script = lib.getExe pkgs.boddle; + }; + }; + + per-system = fn: builtins.mapAttrs (_: fn) nixpkgs.legacyPackages; + + package = pkgs: pkgs.buildGoModule { + meta = { + description = "boddle, in Anlehnung an bottle"; + homepage = "https://git.fbs42.ddnss.de/forgejo/fbs42/boddle"; + name = "boddle"; + }; + pname = "boddle"; + version = "v0.0.1"; + vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + src = pkgs.fetchgit { + url = "https://git.fbs42.ddnss.de/forgejo/fbs42/boddle"; + hash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + }; + }; + + devshell = pkgs: pkgs.mkShell { + nativeBuildInputs = with pkgs.buildPackages; [ + go + ]; + }; + + in { + nixosModules = { + default = self.nixosModules.boddle; + boddle = module; + }; + + overlay = final: prev: { + boddle = package final; + }; + + packages = per-system (pkgs: { + default = package pkgs; + boddle = package pkgs; + }); + + apps = self.packages; + + devShells = per-system (pkgs: { + default = devshell pkgs; + }); + }; +}