jnfilter @ c110c8aaf3cf4c2762e7a3a52ad30623d00f9da3

feat: Rewrite to golang

I have found a nice lib in go to handle XML so I migrate to it.
Go is ages easier to deploy then python.
   1diff --git a/.gitignore b/.gitignore
   2index 74e5fbcaf2bd3aa45229c6601e22ec7ac68d535b..e660fd93d3196215552065b1e63bf6a2f393ed86 100644
   3--- a/.gitignore
   4+++ b/.gitignore
   5@@ -1,3 +1 @@
   6-.idea/
   7-__pycache__/
   8-build/
   9+bin/
  10diff --git a/LICENSE b/LICENSE
  11index 588d327cb48b7636ddc55620254b780856f2e4bd..d5445e7ac8422305d107420de4ab8e1ee6227cca 100644
  12--- a/LICENSE
  13+++ b/LICENSE
  14@@ -1,22 +1,661 @@
  15-MIT License
  16+GNU AFFERO GENERAL PUBLIC LICENSE
  17+                       Version 3, 19 November 2007
  18+
  19+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  20+ Everyone is permitted to copy and distribute verbatim copies
  21+ of this license document, but changing it is not allowed.
  22+
  23+                            Preamble
  24+
  25+  The GNU Affero General Public License is a free, copyleft license for
  26+software and other kinds of works, specifically designed to ensure
  27+cooperation with the community in the case of network server software.
  28+
  29+  The licenses for most software and other practical works are designed
  30+to take away your freedom to share and change the works.  By contrast,
  31+our General Public Licenses are intended to guarantee your freedom to
  32+share and change all versions of a program--to make sure it remains free
  33+software for all its users.
  34+
  35+  When we speak of free software, we are referring to freedom, not
  36+price.  Our General Public Licenses are designed to make sure that you
  37+have the freedom to distribute copies of free software (and charge for
  38+them if you wish), that you receive source code or can get it if you
  39+want it, that you can change the software or use pieces of it in new
  40+free programs, and that you know you can do these things.
  41+
  42+  Developers that use our General Public Licenses protect your rights
  43+with two steps: (1) assert copyright on the software, and (2) offer
  44+you this License which gives you legal permission to copy, distribute
  45+and/or modify the software.
  46+
  47+  A secondary benefit of defending all users' freedom is that
  48+improvements made in alternate versions of the program, if they
  49+receive widespread use, become available for other developers to
  50+incorporate.  Many developers of free software are heartened and
  51+encouraged by the resulting cooperation.  However, in the case of
  52+software used on network servers, this result may fail to come about.
  53+The GNU General Public License permits making a modified version and
  54+letting the public access it on a server without ever releasing its
  55+source code to the public.
  56+
  57+  The GNU Affero General Public License is designed specifically to
  58+ensure that, in such cases, the modified source code becomes available
  59+to the community.  It requires the operator of a network server to
  60+provide the source code of the modified version running there to the
  61+users of that server.  Therefore, public use of a modified version, on
  62+a publicly accessible server, gives the public access to the source
  63+code of the modified version.
  64+
  65+  An older license, called the Affero General Public License and
  66+published by Affero, was designed to accomplish similar goals.  This is
  67+a different license, not a version of the Affero GPL, but Affero has
  68+released a new version of the Affero GPL which permits relicensing under
  69+this license.
  70+
  71+  The precise terms and conditions for copying, distribution and
  72+modification follow.
  73+
  74+                       TERMS AND CONDITIONS
  75+
  76+  0. Definitions.
  77+
  78+  "This License" refers to version 3 of the GNU Affero General Public License.
  79+
  80+  "Copyright" also means copyright-like laws that apply to other kinds of
  81+works, such as semiconductor masks.
  82+
  83+  "The Program" refers to any copyrightable work licensed under this
  84+License.  Each licensee is addressed as "you".  "Licensees" and
  85+"recipients" may be individuals or organizations.
  86+
  87+  To "modify" a work means to copy from or adapt all or part of the work
  88+in a fashion requiring copyright permission, other than the making of an
  89+exact copy.  The resulting work is called a "modified version" of the
  90+earlier work or a work "based on" the earlier work.
  91+
  92+  A "covered work" means either the unmodified Program or a work based
  93+on the Program.
  94+
  95+  To "propagate" a work means to do anything with it that, without
  96+permission, would make you directly or secondarily liable for
  97+infringement under applicable copyright law, except executing it on a
  98+computer or modifying a private copy.  Propagation includes copying,
  99+distribution (with or without modification), making available to the
 100+public, and in some countries other activities as well.
 101+
 102+  To "convey" a work means any kind of propagation that enables other
 103+parties to make or receive copies.  Mere interaction with a user through
 104+a computer network, with no transfer of a copy, is not conveying.
 105+
 106+  An interactive user interface displays "Appropriate Legal Notices"
 107+to the extent that it includes a convenient and prominently visible
 108+feature that (1) displays an appropriate copyright notice, and (2)
 109+tells the user that there is no warranty for the work (except to the
 110+extent that warranties are provided), that licensees may convey the
 111+work under this License, and how to view a copy of this License.  If
 112+the interface presents a list of user commands or options, such as a
 113+menu, a prominent item in the list meets this criterion.
 114+
 115+  1. Source Code.
 116+
 117+  The "source code" for a work means the preferred form of the work
 118+for making modifications to it.  "Object code" means any non-source
 119+form of a work.
 120+
 121+  A "Standard Interface" means an interface that either is an official
 122+standard defined by a recognized standards body, or, in the case of
 123+interfaces specified for a particular programming language, one that
 124+is widely used among developers working in that language.
 125+
 126+  The "System Libraries" of an executable work include anything, other
 127+than the work as a whole, that (a) is included in the normal form of
 128+packaging a Major Component, but which is not part of that Major
 129+Component, and (b) serves only to enable use of the work with that
 130+Major Component, or to implement a Standard Interface for which an
 131+implementation is available to the public in source code form.  A
 132+"Major Component", in this context, means a major essential component
 133+(kernel, window system, and so on) of the specific operating system
 134+(if any) on which the executable work runs, or a compiler used to
 135+produce the work, or an object code interpreter used to run it.
 136+
 137+  The "Corresponding Source" for a work in object code form means all
 138+the source code needed to generate, install, and (for an executable
 139+work) run the object code and to modify the work, including scripts to
 140+control those activities.  However, it does not include the work's
 141+System Libraries, or general-purpose tools or generally available free
 142+programs which are used unmodified in performing those activities but
 143+which are not part of the work.  For example, Corresponding Source
 144+includes interface definition files associated with source files for
 145+the work, and the source code for shared libraries and dynamically
 146+linked subprograms that the work is specifically designed to require,
 147+such as by intimate data communication or control flow between those
 148+subprograms and other parts of the work.
 149+
 150+  The Corresponding Source need not include anything that users
 151+can regenerate automatically from other parts of the Corresponding
 152+Source.
 153+
 154+  The Corresponding Source for a work in source code form is that
 155+same work.
 156+
 157+  2. Basic Permissions.
 158+
 159+  All rights granted under this License are granted for the term of
 160+copyright on the Program, and are irrevocable provided the stated
 161+conditions are met.  This License explicitly affirms your unlimited
 162+permission to run the unmodified Program.  The output from running a
 163+covered work is covered by this License only if the output, given its
 164+content, constitutes a covered work.  This License acknowledges your
 165+rights of fair use or other equivalent, as provided by copyright law.
 166+
 167+  You may make, run and propagate covered works that you do not
 168+convey, without conditions so long as your license otherwise remains
 169+in force.  You may convey covered works to others for the sole purpose
 170+of having them make modifications exclusively for you, or provide you
 171+with facilities for running those works, provided that you comply with
 172+the terms of this License in conveying all material for which you do
 173+not control copyright.  Those thus making or running the covered works
 174+for you must do so exclusively on your behalf, under your direction
 175+and control, on terms that prohibit them from making any copies of
 176+your copyrighted material outside their relationship with you.
 177+
 178+  Conveying under any other circumstances is permitted solely under
 179+the conditions stated below.  Sublicensing is not allowed; section 10
 180+makes it unnecessary.
 181+
 182+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
 183+
 184+  No covered work shall be deemed part of an effective technological
 185+measure under any applicable law fulfilling obligations under article
 186+11 of the WIPO copyright treaty adopted on 20 December 1996, or
 187+similar laws prohibiting or restricting circumvention of such
 188+measures.
 189+
 190+  When you convey a covered work, you waive any legal power to forbid
 191+circumvention of technological measures to the extent such circumvention
 192+is effected by exercising rights under this License with respect to
 193+the covered work, and you disclaim any intention to limit operation or
 194+modification of the work as a means of enforcing, against the work's
 195+users, your or third parties' legal rights to forbid circumvention of
 196+technological measures.
 197+
 198+  4. Conveying Verbatim Copies.
 199+
 200+  You may convey verbatim copies of the Program's source code as you
 201+receive it, in any medium, provided that you conspicuously and
 202+appropriately publish on each copy an appropriate copyright notice;
 203+keep intact all notices stating that this License and any
 204+non-permissive terms added in accord with section 7 apply to the code;
 205+keep intact all notices of the absence of any warranty; and give all
 206+recipients a copy of this License along with the Program.
 207+
 208+  You may charge any price or no price for each copy that you convey,
 209+and you may offer support or warranty protection for a fee.
 210+
 211+  5. Conveying Modified Source Versions.
 212+
 213+  You may convey a work based on the Program, or the modifications to
 214+produce it from the Program, in the form of source code under the
 215+terms of section 4, provided that you also meet all of these conditions:
 216+
 217+    a) The work must carry prominent notices stating that you modified
 218+    it, and giving a relevant date.
 219+
 220+    b) The work must carry prominent notices stating that it is
 221+    released under this License and any conditions added under section
 222+    7.  This requirement modifies the requirement in section 4 to
 223+    "keep intact all notices".
 224+
 225+    c) You must license the entire work, as a whole, under this
 226+    License to anyone who comes into possession of a copy.  This
 227+    License will therefore apply, along with any applicable section 7
 228+    additional terms, to the whole of the work, and all its parts,
 229+    regardless of how they are packaged.  This License gives no
 230+    permission to license the work in any other way, but it does not
 231+    invalidate such permission if you have separately received it.
 232+
 233+    d) If the work has interactive user interfaces, each must display
 234+    Appropriate Legal Notices; however, if the Program has interactive
 235+    interfaces that do not display Appropriate Legal Notices, your
 236+    work need not make them do so.
 237+
 238+  A compilation of a covered work with other separate and independent
 239+works, which are not by their nature extensions of the covered work,
 240+and which are not combined with it such as to form a larger program,
 241+in or on a volume of a storage or distribution medium, is called an
 242+"aggregate" if the compilation and its resulting copyright are not
 243+used to limit the access or legal rights of the compilation's users
 244+beyond what the individual works permit.  Inclusion of a covered work
 245+in an aggregate does not cause this License to apply to the other
 246+parts of the aggregate.
 247+
 248+  6. Conveying Non-Source Forms.
 249+
 250+  You may convey a covered work in object code form under the terms
 251+of sections 4 and 5, provided that you also convey the
 252+machine-readable Corresponding Source under the terms of this License,
 253+in one of these ways:
 254+
 255+    a) Convey the object code in, or embodied in, a physical product
 256+    (including a physical distribution medium), accompanied by the
 257+    Corresponding Source fixed on a durable physical medium
 258+    customarily used for software interchange.
 259+
 260+    b) Convey the object code in, or embodied in, a physical product
 261+    (including a physical distribution medium), accompanied by a
 262+    written offer, valid for at least three years and valid for as
 263+    long as you offer spare parts or customer support for that product
 264+    model, to give anyone who possesses the object code either (1) a
 265+    copy of the Corresponding Source for all the software in the
 266+    product that is covered by this License, on a durable physical
 267+    medium customarily used for software interchange, for a price no
 268+    more than your reasonable cost of physically performing this
 269+    conveying of source, or (2) access to copy the
 270+    Corresponding Source from a network server at no charge.
 271+
 272+    c) Convey individual copies of the object code with a copy of the
 273+    written offer to provide the Corresponding Source.  This
 274+    alternative is allowed only occasionally and noncommercially, and
 275+    only if you received the object code with such an offer, in accord
 276+    with subsection 6b.
 277+
 278+    d) Convey the object code by offering access from a designated
 279+    place (gratis or for a charge), and offer equivalent access to the
 280+    Corresponding Source in the same way through the same place at no
 281+    further charge.  You need not require recipients to copy the
 282+    Corresponding Source along with the object code.  If the place to
 283+    copy the object code is a network server, the Corresponding Source
 284+    may be on a different server (operated by you or a third party)
 285+    that supports equivalent copying facilities, provided you maintain
 286+    clear directions next to the object code saying where to find the
 287+    Corresponding Source.  Regardless of what server hosts the
 288+    Corresponding Source, you remain obligated to ensure that it is
 289+    available for as long as needed to satisfy these requirements.
 290+
 291+    e) Convey the object code using peer-to-peer transmission, provided
 292+    you inform other peers where the object code and Corresponding
 293+    Source of the work are being offered to the general public at no
 294+    charge under subsection 6d.
 295+
 296+  A separable portion of the object code, whose source code is excluded
 297+from the Corresponding Source as a System Library, need not be
 298+included in conveying the object code work.
 299+
 300+  A "User Product" is either (1) a "consumer product", which means any
 301+tangible personal property which is normally used for personal, family,
 302+or household purposes, or (2) anything designed or sold for incorporation
 303+into a dwelling.  In determining whether a product is a consumer product,
 304+doubtful cases shall be resolved in favor of coverage.  For a particular
 305+product received by a particular user, "normally used" refers to a
 306+typical or common use of that class of product, regardless of the status
 307+of the particular user or of the way in which the particular user
 308+actually uses, or expects or is expected to use, the product.  A product
 309+is a consumer product regardless of whether the product has substantial
 310+commercial, industrial or non-consumer uses, unless such uses represent
 311+the only significant mode of use of the product.
 312+
 313+  "Installation Information" for a User Product means any methods,
 314+procedures, authorization keys, or other information required to install
 315+and execute modified versions of a covered work in that User Product from
 316+a modified version of its Corresponding Source.  The information must
 317+suffice to ensure that the continued functioning of the modified object
 318+code is in no case prevented or interfered with solely because
 319+modification has been made.
 320+
 321+  If you convey an object code work under this section in, or with, or
 322+specifically for use in, a User Product, and the conveying occurs as
 323+part of a transaction in which the right of possession and use of the
 324+User Product is transferred to the recipient in perpetuity or for a
 325+fixed term (regardless of how the transaction is characterized), the
 326+Corresponding Source conveyed under this section must be accompanied
 327+by the Installation Information.  But this requirement does not apply
 328+if neither you nor any third party retains the ability to install
 329+modified object code on the User Product (for example, the work has
 330+been installed in ROM).
 331+
 332+  The requirement to provide Installation Information does not include a
 333+requirement to continue to provide support service, warranty, or updates
 334+for a work that has been modified or installed by the recipient, or for
 335+the User Product in which it has been modified or installed.  Access to a
 336+network may be denied when the modification itself materially and
 337+adversely affects the operation of the network or violates the rules and
 338+protocols for communication across the network.
 339+
 340+  Corresponding Source conveyed, and Installation Information provided,
 341+in accord with this section must be in a format that is publicly
 342+documented (and with an implementation available to the public in
 343+source code form), and must require no special password or key for
 344+unpacking, reading or copying.
 345+
 346+  7. Additional Terms.
 347+
 348+  "Additional permissions" are terms that supplement the terms of this
 349+License by making exceptions from one or more of its conditions.
 350+Additional permissions that are applicable to the entire Program shall
 351+be treated as though they were included in this License, to the extent
 352+that they are valid under applicable law.  If additional permissions
 353+apply only to part of the Program, that part may be used separately
 354+under those permissions, but the entire Program remains governed by
 355+this License without regard to the additional permissions.
 356+
 357+  When you convey a copy of a covered work, you may at your option
 358+remove any additional permissions from that copy, or from any part of
 359+it.  (Additional permissions may be written to require their own
 360+removal in certain cases when you modify the work.)  You may place
 361+additional permissions on material, added by you to a covered work,
 362+for which you have or can give appropriate copyright permission.
 363+
 364+  Notwithstanding any other provision of this License, for material you
 365+add to a covered work, you may (if authorized by the copyright holders of
 366+that material) supplement the terms of this License with terms:
 367+
 368+    a) Disclaiming warranty or limiting liability differently from the
 369+    terms of sections 15 and 16 of this License; or
 370+
 371+    b) Requiring preservation of specified reasonable legal notices or
 372+    author attributions in that material or in the Appropriate Legal
 373+    Notices displayed by works containing it; or
 374+
 375+    c) Prohibiting misrepresentation of the origin of that material, or
 376+    requiring that modified versions of such material be marked in
 377+    reasonable ways as different from the original version; or
 378+
 379+    d) Limiting the use for publicity purposes of names of licensors or
 380+    authors of the material; or
 381+
 382+    e) Declining to grant rights under trademark law for use of some
 383+    trade names, trademarks, or service marks; or
 384+
 385+    f) Requiring indemnification of licensors and authors of that
 386+    material by anyone who conveys the material (or modified versions of
 387+    it) with contractual assumptions of liability to the recipient, for
 388+    any liability that these contractual assumptions directly impose on
 389+    those licensors and authors.
 390+
 391+  All other non-permissive additional terms are considered "further
 392+restrictions" within the meaning of section 10.  If the Program as you
 393+received it, or any part of it, contains a notice stating that it is
 394+governed by this License along with a term that is a further
 395+restriction, you may remove that term.  If a license document contains
 396+a further restriction but permits relicensing or conveying under this
 397+License, you may add to a covered work material governed by the terms
 398+of that license document, provided that the further restriction does
 399+not survive such relicensing or conveying.
 400+
 401+  If you add terms to a covered work in accord with this section, you
 402+must place, in the relevant source files, a statement of the
 403+additional terms that apply to those files, or a notice indicating
 404+where to find the applicable terms.
 405+
 406+  Additional terms, permissive or non-permissive, may be stated in the
 407+form of a separately written license, or stated as exceptions;
 408+the above requirements apply either way.
 409+
 410+  8. Termination.
 411+
 412+  You may not propagate or modify a covered work except as expressly
 413+provided under this License.  Any attempt otherwise to propagate or
 414+modify it is void, and will automatically terminate your rights under
 415+this License (including any patent licenses granted under the third
 416+paragraph of section 11).
 417+
 418+  However, if you cease all violation of this License, then your
 419+license from a particular copyright holder is reinstated (a)
 420+provisionally, unless and until the copyright holder explicitly and
 421+finally terminates your license, and (b) permanently, if the copyright
 422+holder fails to notify you of the violation by some reasonable means
 423+prior to 60 days after the cessation.
 424+
 425+  Moreover, your license from a particular copyright holder is
 426+reinstated permanently if the copyright holder notifies you of the
 427+violation by some reasonable means, this is the first time you have
 428+received notice of violation of this License (for any work) from that
 429+copyright holder, and you cure the violation prior to 30 days after
 430+your receipt of the notice.
 431+
 432+  Termination of your rights under this section does not terminate the
 433+licenses of parties who have received copies or rights from you under
 434+this License.  If your rights have been terminated and not permanently
 435+reinstated, you do not qualify to receive new licenses for the same
 436+material under section 10.
 437+
 438+  9. Acceptance Not Required for Having Copies.
 439+
 440+  You are not required to accept this License in order to receive or
 441+run a copy of the Program.  Ancillary propagation of a covered work
 442+occurring solely as a consequence of using peer-to-peer transmission
 443+to receive a copy likewise does not require acceptance.  However,
 444+nothing other than this License grants you permission to propagate or
 445+modify any covered work.  These actions infringe copyright if you do
 446+not accept this License.  Therefore, by modifying or propagating a
 447+covered work, you indicate your acceptance of this License to do so.
 448+
 449+  10. Automatic Licensing of Downstream Recipients.
 450+
 451+  Each time you convey a covered work, the recipient automatically
 452+receives a license from the original licensors, to run, modify and
 453+propagate that work, subject to this License.  You are not responsible
 454+for enforcing compliance by third parties with this License.
 455+
 456+  An "entity transaction" is a transaction transferring control of an
 457+organization, or substantially all assets of one, or subdividing an
 458+organization, or merging organizations.  If propagation of a covered
 459+work results from an entity transaction, each party to that
 460+transaction who receives a copy of the work also receives whatever
 461+licenses to the work the party's predecessor in interest had or could
 462+give under the previous paragraph, plus a right to possession of the
 463+Corresponding Source of the work from the predecessor in interest, if
 464+the predecessor has it or can get it with reasonable efforts.
 465+
 466+  You may not impose any further restrictions on the exercise of the
 467+rights granted or affirmed under this License.  For example, you may
 468+not impose a license fee, royalty, or other charge for exercise of
 469+rights granted under this License, and you may not initiate litigation
 470+(including a cross-claim or counterclaim in a lawsuit) alleging that
 471+any patent claim is infringed by making, using, selling, offering for
 472+sale, or importing the Program or any portion of it.
 473+
 474+  11. Patents.
 475+
 476+  A "contributor" is a copyright holder who authorizes use under this
 477+License of the Program or a work on which the Program is based.  The
 478+work thus licensed is called the contributor's "contributor version".
 479+
 480+  A contributor's "essential patent claims" are all patent claims
 481+owned or controlled by the contributor, whether already acquired or
 482+hereafter acquired, that would be infringed by some manner, permitted
 483+by this License, of making, using, or selling its contributor version,
 484+but do not include claims that would be infringed only as a
 485+consequence of further modification of the contributor version.  For
 486+purposes of this definition, "control" includes the right to grant
 487+patent sublicenses in a manner consistent with the requirements of
 488+this License.
 489+
 490+  Each contributor grants you a non-exclusive, worldwide, royalty-free
 491+patent license under the contributor's essential patent claims, to
 492+make, use, sell, offer for sale, import and otherwise run, modify and
 493+propagate the contents of its contributor version.
 494+
 495+  In the following three paragraphs, a "patent license" is any express
 496+agreement or commitment, however denominated, not to enforce a patent
 497+(such as an express permission to practice a patent or covenant not to
 498+sue for patent infringement).  To "grant" such a patent license to a
 499+party means to make such an agreement or commitment not to enforce a
 500+patent against the party.
 501+
 502+  If you convey a covered work, knowingly relying on a patent license,
 503+and the Corresponding Source of the work is not available for anyone
 504+to copy, free of charge and under the terms of this License, through a
 505+publicly available network server or other readily accessible means,
 506+then you must either (1) cause the Corresponding Source to be so
 507+available, or (2) arrange to deprive yourself of the benefit of the
 508+patent license for this particular work, or (3) arrange, in a manner
 509+consistent with the requirements of this License, to extend the patent
 510+license to downstream recipients.  "Knowingly relying" means you have
 511+actual knowledge that, but for the patent license, your conveying the
 512+covered work in a country, or your recipient's use of the covered work
 513+in a country, would infringe one or more identifiable patents in that
 514+country that you have reason to believe are valid.
 515+
 516+  If, pursuant to or in connection with a single transaction or
 517+arrangement, you convey, or propagate by procuring conveyance of, a
 518+covered work, and grant a patent license to some of the parties
 519+receiving the covered work authorizing them to use, propagate, modify
 520+or convey a specific copy of the covered work, then the patent license
 521+you grant is automatically extended to all recipients of the covered
 522+work and works based on it.
 523 
 524-Copyright (c) 2021 Gabriel Arakaki Giovanini
 525+  A patent license is "discriminatory" if it does not include within
 526+the scope of its coverage, prohibits the exercise of, or is
 527+conditioned on the non-exercise of one or more of the rights that are
 528+specifically granted under this License.  You may not convey a covered
 529+work if you are a party to an arrangement with a third party that is
 530+in the business of distributing software, under which you make payment
 531+to the third party based on the extent of your activity of conveying
 532+the work, and under which the third party grants, to any of the
 533+parties who would receive the covered work from you, a discriminatory
 534+patent license (a) in connection with copies of the covered work
 535+conveyed by you (or copies made from those copies), or (b) primarily
 536+for and in connection with specific products or compilations that
 537+contain the covered work, unless you entered into that arrangement,
 538+or that patent license was granted, prior to 28 March 2007.
 539 
 540-Permission is hereby granted, free of charge, to any person obtaining a copy of
 541-this software and associated documentation files (the "Software"), to deal in
 542-the Software without restriction, including without limitation the rights to
 543-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 544-of the Software, and to permit persons to whom the Software is furnished to do
 545-so, subject to the following conditions:
 546+  Nothing in this License shall be construed as excluding or limiting
 547+any implied license or other defenses to infringement that may
 548+otherwise be available to you under applicable patent law.
 549 
 550-The above copyright notice and this permission notice (including the next
 551-        paragraph) shall be included in all copies or substantial portions of the
 552-Software.
 553+  12. No Surrender of Others' Freedom.
 554 
 555-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 556-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 557-    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 558-    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 559-    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 560-    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 561-    SOFTWARE.
 562+  If conditions are imposed on you (whether by court order, agreement or
 563+otherwise) that contradict the conditions of this License, they do not
 564+excuse you from the conditions of this License.  If you cannot convey a
 565+covered work so as to satisfy simultaneously your obligations under this
 566+License and any other pertinent obligations, then as a consequence you may
 567+not convey it at all.  For example, if you agree to terms that obligate you
 568+to collect a royalty for further conveying from those to whom you convey
 569+the Program, the only way you could satisfy both those terms and this
 570+License would be to refrain entirely from conveying the Program.
 571+
 572+  13. Remote Network Interaction; Use with the GNU General Public License.
 573+
 574+  Notwithstanding any other provision of this License, if you modify the
 575+Program, your modified version must prominently offer all users
 576+interacting with it remotely through a computer network (if your version
 577+supports such interaction) an opportunity to receive the Corresponding
 578+Source of your version by providing access to the Corresponding Source
 579+from a network server at no charge, through some standard or customary
 580+means of facilitating copying of software.  This Corresponding Source
 581+shall include the Corresponding Source for any work covered by version 3
 582+of the GNU General Public License that is incorporated pursuant to the
 583+following paragraph.
 584+
 585+  Notwithstanding any other provision of this License, you have
 586+permission to link or combine any covered work with a work licensed
 587+under version 3 of the GNU General Public License into a single
 588+combined work, and to convey the resulting work.  The terms of this
 589+License will continue to apply to the part which is the covered work,
 590+but the work with which it is combined will remain governed by version
 591+3 of the GNU General Public License.
 592+
 593+  14. Revised Versions of this License.
 594+
 595+  The Free Software Foundation may publish revised and/or new versions of
 596+the GNU Affero General Public License from time to time.  Such new versions
 597+will be similar in spirit to the present version, but may differ in detail to
 598+address new problems or concerns.
 599+
 600+  Each version is given a distinguishing version number.  If the
 601+Program specifies that a certain numbered version of the GNU Affero General
 602+Public License "or any later version" applies to it, you have the
 603+option of following the terms and conditions either of that numbered
 604+version or of any later version published by the Free Software
 605+Foundation.  If the Program does not specify a version number of the
 606+GNU Affero General Public License, you may choose any version ever published
 607+by the Free Software Foundation.
 608+
 609+  If the Program specifies that a proxy can decide which future
 610+versions of the GNU Affero General Public License can be used, that proxy's
 611+public statement of acceptance of a version permanently authorizes you
 612+to choose that version for the Program.
 613+
 614+  Later license versions may give you additional or different
 615+permissions.  However, no additional obligations are imposed on any
 616+author or copyright holder as a result of your choosing to follow a
 617+later version.
 618+
 619+  15. Disclaimer of Warranty.
 620+
 621+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
 622+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
 623+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
 624+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
 625+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 626+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
 627+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
 628+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 629+
 630+  16. Limitation of Liability.
 631+
 632+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 633+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
 634+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
 635+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
 636+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
 637+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
 638+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
 639+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
 640+SUCH DAMAGES.
 641+
 642+  17. Interpretation of Sections 15 and 16.
 643+
 644+  If the disclaimer of warranty and limitation of liability provided
 645+above cannot be given local legal effect according to their terms,
 646+reviewing courts shall apply local law that most closely approximates
 647+an absolute waiver of all civil liability in connection with the
 648+Program, unless a warranty or assumption of liability accompanies a
 649+copy of the Program in return for a fee.
 650+
 651+                     END OF TERMS AND CONDITIONS
 652+
 653+            How to Apply These Terms to Your New Programs
 654+
 655+  If you develop a new program, and you want it to be of the greatest
 656+possible use to the public, the best way to achieve this is to make it
 657+free software which everyone can redistribute and change under these terms.
 658+
 659+  To do so, attach the following notices to the program.  It is safest
 660+to attach them to the start of each source file to most effectively
 661+state the exclusion of warranty; and each file should have at least
 662+the "copyright" line and a pointer to where the full notice is found.
 663+
 664+    <one line to give the program's name and a brief idea of what it does.>
 665+    Copyright (C) <year>  <name of author>
 666+
 667+    This program is free software: you can redistribute it and/or modify
 668+    it under the terms of the GNU Affero General Public License as published by
 669+    the Free Software Foundation, either version 3 of the License, or
 670+    (at your option) any later version.
 671+
 672+    This program is distributed in the hope that it will be useful,
 673+    but WITHOUT ANY WARRANTY; without even the implied warranty of
 674+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 675+    GNU Affero General Public License for more details.
 676+
 677+    You should have received a copy of the GNU Affero General Public License
 678+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 679+
 680+Also add information on how to contact you by electronic and paper mail.
 681+
 682+  If your software can interact with users remotely through a computer
 683+network, you should also make sure that it provides a way for users to
 684+get its source.  For example, if your program is a web application, its
 685+interface could display a "Source" link that leads users to an archive
 686+of the code.  There are many ways you could offer source, and different
 687+solutions will be better for different programs; see section 13 for the
 688+specific requirements.
 689+
 690+  You should also get your employer (if you work as a programmer) or school,
 691+if any, to sign a "copyright disclaimer" for the program, if necessary.
 692+For more information on this, and how to apply and follow the GNU AGPL, see
 693+<http://www.gnu.org/licenses/>.
 694diff --git a/Makefile b/Makefile
 695index 7922953830d2d995087f56a881b41f626b1a6a4c..69b82d9ae26675346d7810a090d1e10a0be7e7b3 100644
 696--- a/Makefile
 697+++ b/Makefile
 698@@ -1,25 +1,26 @@
 699+BIN?=jnfilter
 700+PREFIX?=/usr/local
 701+BINDIR?=$(PREFIX)/bin
 702 
 703-rpm: rpm_dist
 704-	rpmbuild -bb \
 705-		./build/bdist.linux-x86_64/rpm/SPECS/jnfilter.spec \
 706-		--define "_topdir $(PWD)/build/bdist.linux-x86_64/rpm/"
 707+OUT=./bin/$(BIN)
 708+SERVER=./main.go
 709 
 710-rpm_dist:
 711-	python setup.py bdist_rpm
 712+GO_BUILD=go build -v -ldflags '-w -s'
 713+GO_RUN=go run -v
 714+all: build
 715+
 716+install: build
 717+	install -Dm755 $(OUT) $(BINDIR)/$(BIN)
 718+
 719+build:
 720+	$(GO_BUILD) -o $(OUT) $(SERVER)
 721 
 722-clean:
 723-	python setup.py clean --all
 724-	rm -rf dist jnfilter.egg-info
 725+compress: build
 726+	upx -1 $(OUT)
 727 
 728-docs:
 729-	pandoc -s \
 730-		--include-in-header=docs/bamboo.min.css \
 731-		--metadata title="Filtro para Nerdcast" \
 732-		--template docs/template.html \
 733-		-s README.md \
 734-		-o index.html
 735+compress_into_oblivion: build
 736+	upx --best --ultra-brute $(OUT)
 737 
 738-install_deps:
 739-	dnf install -y rpmdevtools rpmlint python
 740+run: sass tmpl
 741+	$(GO_RUN) $(SERVER)
 742 
 743-.PHONY: docs
 744diff --git a/Procfile b/Procfile
 745deleted file mode 100644
 746index 60ee5896c1c4de0851455a9fd9652569b36ee612..0000000000000000000000000000000000000000
 747--- a/Procfile
 748+++ /dev/null
 749@@ -1 +0,0 @@
 750-web: uvicorn main:app --host=0.0.0.0 --port=${PORT}
 751diff --git a/README.md b/README.md
 752index 30a5a41f93c90024e19188169332f1920655b944..2432f102038bf258a12ba332d2852da561f5f72f 100644
 753--- a/README.md
 754+++ b/README.md
 755@@ -59,18 +59,3 @@ parâmetro, se tiver `nerdcast,mamicas` troque para `mamicas,nercast`
 756 
 757 o ideal e que cliente de podcast nao obrigue a fazer isso mas fazer o que as
 758 outras opções fazem pior.
 759-
 760-## Para programadores
 761-
 762-E um projeto simples feito em cima do FastApi. Ele vai pegar o _feed_ e filtrar
 763-os itens do _feed_ do podcast. Não tem cache nem nada sendo armazenado, todo
 764-processamento e feito a partir do feed para cada requisição.
 765-
 766-Para rodar basta instalar os requirements e rodar o seguinte código:
 767-
 768-```shell
 769-uvicorn main:app --host=0.0.0.0
 770-```
 771-
 772-E você já pode apontar o seu agregador favorito para sua maquina.
 773-
 774diff --git a/build.yml b/build.yml
 775deleted file mode 100644
 776index 10616d05b73e4ddbe3c7e91981b59a3db1771aaa..0000000000000000000000000000000000000000
 777--- a/build.yml
 778+++ /dev/null
 779@@ -1,25 +0,0 @@
 780-image: alpine/latest
 781-packages:
 782-  - rsync
 783-
 784-sources:
 785-  - https://git.sr.ht/~gabrielgio/jnfilter
 786-
 787-secrets:
 788-  - 008c4f67-b864-47f8-9790-cd32f2ae8516
 789-
 790-environment:
 791-  build: builds@gabrielgio.me
 792-  artifact: artifacts.gabrielgio.me/archive/jnfilter/
 793-  version: 0.1.0
 794-
 795-tasks:
 796-  - archive: |
 797-      cd jnfilter
 798-      git archive \
 799-          -o jnfilter-$version.tar.gz \
 800-          --prefix=jnfilter-$version/ HEAD
 801-  - deploy_archive: |
 802-      cd jnfilter
 803-      sshopts="ssh -o StrictHostKeyChecking=no"
 804-      rsync --mkpath --rsh="$sshopts" -rP *.tar.* $build:/var/www/$artifact
 805diff --git a/docs/bamboo.min.css b/docs/bamboo.min.css
 806deleted file mode 100644
 807index 5289de7f3c2965e1de6f5866adf9756c92f4c3f5..0000000000000000000000000000000000000000
 808--- a/docs/bamboo.min.css
 809+++ /dev/null
 810@@ -1,369 +0,0 @@
 811-<style type="text/css">
 812-:root {
 813-    --b-font-main: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
 814-    --b-font-mono: Consolas, Monaco, monospace;
 815-    --b-txt: #2e3440;
 816-    --b-bg-1: #fff;
 817-    --b-bg-2: #eceff4;
 818-    --b-line: #eceff4;
 819-    --b-link: #bf616a;
 820-    --b-btn-bg: #242933;
 821-    --b-btn-txt: #fff;
 822-    --b-focus: #88c0d0
 823-}
 824-
 825-@media (prefers-color-scheme: dark) {
 826-    :root {
 827-        --b-txt: #eceff4;
 828-        --b-bg-1: #2e3440;
 829-        --b-bg-2: #3b4252;
 830-        --b-line: #3b4252
 831-    }
 832-}
 833-
 834-*, :after, :before {
 835-    box-sizing: border-box
 836-}
 837-
 838-html:focus-within {
 839-    scroll-behavior: smooth
 840-}
 841-
 842-body {
 843-    max-width: 70ch;
 844-    padding: 0 1rem;
 845-    margin: auto;
 846-    background: var(--b-bg-1);
 847-    font-family: var(--b-font-main);
 848-    text-rendering: optimizeSpeed;
 849-    line-height: 1.5;
 850-    color: var(--b-txt);
 851-    -moz-tab-size: 4;
 852-    tab-size: 4;
 853-    word-break: break-word;
 854-    -webkit-tap-highlight-color: transparent;
 855-    -webkit-text-size-adjust: 100%
 856-}
 857-
 858-address, audio, blockquote, dd, details, dl, fieldset, figure, h1, h2, h3, h4, h5, h6, hr, iframe, ol, p, pre, table, ul, video {
 859-    margin: 0 0 1.5rem
 860-}
 861-
 862-h1, h2, h3, h4, h5, h6 {
 863-    line-height: 1.25;
 864-    margin-top: 2rem
 865-}
 866-
 867-h1 {
 868-    font-size: 2rem
 869-}
 870-
 871-h2 {
 872-    font-size: 1.5rem
 873-}
 874-
 875-h3 {
 876-    font-size: 1.25rem
 877-}
 878-
 879-h4 {
 880-    font-size: 1rem
 881-}
 882-
 883-h5 {
 884-    font-size: .875rem
 885-}
 886-
 887-h6 {
 888-    font-size: .75rem
 889-}
 890-
 891-a {
 892-    color: var(--b-link);
 893-    text-decoration: none
 894-}
 895-
 896-a:hover {
 897-    text-decoration: underline
 898-}
 899-
 900-img, svg, video {
 901-    height: auto
 902-}
 903-
 904-embed, iframe, img, object, svg, video {
 905-    max-width: 100%
 906-}
 907-
 908-iframe {
 909-    border-style: none
 910-}
 911-
 912-abbr[title] {
 913-    text-decoration: underline;
 914-    text-decoration: underline dotted
 915-}
 916-
 917-blockquote {
 918-    margin-left: 0;
 919-    padding: .5rem 0 .5rem 1.5rem;
 920-    border-left: .25rem solid var(--b-txt)
 921-}
 922-
 923-blockquote > :last-child {
 924-    margin-bottom: 0
 925-}
 926-
 927-small {
 928-    font-size: .875rem
 929-}
 930-
 931-sub, sup {
 932-    font-size: .75em;
 933-    line-height: 0;
 934-    position: relative;
 935-    vertical-align: baseline
 936-}
 937-
 938-sub {
 939-    bottom: -.25em
 940-}
 941-
 942-sup {
 943-    top: -.5em
 944-}
 945-
 946-hr {
 947-    height: 0;
 948-    overflow: visible;
 949-    border: 0;
 950-    border-bottom: 1px solid var(--b-line)
 951-}
 952-
 953-code, kbd, pre, samp, tt, var {
 954-    background: var(--b-bg-2);
 955-    border-radius: .25rem;
 956-    padding: .125rem .25rem;
 957-    font-family: var(--b-font-mono);
 958-    font-size: .875rem
 959-}
 960-
 961-pre {
 962-    padding: 1rem;
 963-    border-radius: 0;
 964-    overflow: auto;
 965-    white-space: pre
 966-}
 967-
 968-pre code {
 969-    padding: 0
 970-}
 971-
 972-details {
 973-    display: block;
 974-    padding: .5rem 1rem;
 975-    background: var(--b-bg-2);
 976-    border: 1px solid var(--b-line);
 977-    border-radius: .25rem
 978-}
 979-
 980-details > :last-child {
 981-    margin-bottom: 0
 982-}
 983-
 984-details[open] > summary {
 985-    margin-bottom: 1.5rem
 986-}
 987-
 988-summary {
 989-    display: list-item;
 990-    cursor: pointer;
 991-    font-weight: 700
 992-}
 993-
 994-summary:focus {
 995-    box-shadow: none
 996-}
 997-
 998-table {
 999-    border-collapse: collapse;
1000-    width: 100%;
1001-    text-indent: 0
1002-}
1003-
1004-table caption {
1005-    margin-bottom: .5rem
1006-}
1007-
1008-tr {
1009-    border-bottom: 1px solid var(--b-line)
1010-}
1011-
1012-td, th {
1013-    padding: .5rem 0
1014-}
1015-
1016-th {
1017-    text-align: left
1018-}
1019-
1020-dd, ol, ul {
1021-    padding-left: 2rem
1022-}
1023-
1024-li > ol, li > ul {
1025-    margin-bottom: 0
1026-}
1027-
1028-fieldset {
1029-    padding: .5rem .75rem;
1030-    border: 1px solid var(--b-line);
1031-    border-radius: .25rem
1032-}
1033-
1034-legend {
1035-    padding: 0 .25rem
1036-}
1037-
1038-button, input, select, textarea {
1039-    margin: 0;
1040-    padding: .5rem .75rem;
1041-    max-width: 100%;
1042-    background: var(--b-bg-2);
1043-    border: 0;
1044-    border-radius: .25rem;
1045-    font: inherit;
1046-    line-height: 1.125;
1047-    color: var(--b-txt)
1048-}
1049-
1050-input:not([size]):not([type=button i]):not([type=submit i]):not([type=reset i]):not([type=checkbox i]):not([type=radio i]), select {
1051-    width: 100%
1052-}
1053-
1054-[type=color i] {
1055-    min-height: 2.125rem
1056-}
1057-
1058-select:not([multiple]):not([size]) {
1059-    padding-right: 1.5rem;
1060-    background-repeat: no-repeat;
1061-    background-position: right .5rem center;
1062-    -moz-appearance: none;
1063-    -webkit-appearance: none;
1064-    appearance: none
1065-}
1066-
1067-textarea {
1068-    width: 100%;
1069-    resize: vertical
1070-}
1071-
1072-textarea:not([rows]) {
1073-    height: 8rem
1074-}
1075-
1076-[type=button i], [type=reset i], [type=submit i], button {
1077-    -webkit-appearance: button;
1078-    display: inline-block;
1079-    text-align: center;
1080-    white-space: nowrap;
1081-    background: var(--b-btn-bg);
1082-    color: var(--b-btn-txt);
1083-    border: 0;
1084-    cursor: pointer;
1085-    transition: opacity .25s
1086-}
1087-
1088-[type=button i]:hover, [type=reset i]:hover, [type=submit i]:hover, button:hover {
1089-    opacity: .75
1090-}
1091-
1092-[type=button i][disabled], [type=reset i][disabled], [type=submit i][disabled], button[disabled] {
1093-    opacity: .5
1094-}
1095-
1096-progress {
1097-    vertical-align: middle
1098-}
1099-
1100-[type=search i] {
1101-    -webkit-appearance: textfield;
1102-    outline-offset: -2px
1103-}
1104-
1105-::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
1106-    height: auto
1107-}
1108-
1109-::-webkit-input-placeholder {
1110-    color: inherit;
1111-    opacity: .5
1112-}
1113-
1114-::-webkit-search-decoration {
1115-    -webkit-appearance: none
1116-}
1117-
1118-::-webkit-file-upload-button {
1119-    -webkit-appearance: button;
1120-    font: inherit
1121-}
1122-
1123-::-moz-focus-inner {
1124-    border-style: none;
1125-    padding: 0
1126-}
1127-
1128-:-moz-focusring {
1129-    outline: 1px dotted ButtonText
1130-}
1131-
1132-:-moz-ui-invalid {
1133-    box-shadow: none
1134-}
1135-
1136-[aria-busy=true i] {
1137-    cursor: progress
1138-}
1139-
1140-[aria-controls] {
1141-    cursor: pointer
1142-}
1143-
1144-[aria-disabled=true i], [disabled] {
1145-    cursor: not-allowed
1146-}
1147-
1148-:focus, details:focus-within {
1149-    outline: none;
1150-    box-shadow: 0 0 0 2px var(--b-focus)
1151-}
1152-
1153-@media (prefers-reduced-motion: reduce) {
1154-    html:focus-within {
1155-        scroll-behavior: auto
1156-    }
1157-
1158-    *, :after, :before {
1159-        animation-delay: -1ms !important;
1160-        animation-duration: 1ms !important;
1161-        animation-iteration-count: 1 !important;
1162-        background-attachment: scroll !important;
1163-        scroll-behavior: auto !important;
1164-        transition-delay: 0 !important;
1165-        transition-duration: 0 !important
1166-    }
1167-}
1168-
1169-select:not([multiple]):not([size]) {
1170-    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='16' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%232e3440'%3E%3Cpath d='M5 6l5 5 5-5 2 1-7 7-7-7 2-1z'/%3E%3C/svg%3E")
1171-}
1172-
1173-@media (prefers-color-scheme: dark) {
1174-    select:not([multiple]):not([size]) {
1175-        background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='16' height='16' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23eceff4'%3E%3Cpath d='M5 6l5 5 5-5 2 1-7 7-7-7 2-1z'/%3E%3C/svg%3E")
1176-    }
1177-}
1178-
1179-</style >
1180\ No newline at end of file
1181diff --git a/docs/template.html b/docs/template.html
1182deleted file mode 100644
1183index 0d6c5d0d0cf84a8f6e8bc6ef3899ddd81e111ff9..0000000000000000000000000000000000000000
1184--- a/docs/template.html
1185+++ /dev/null
1186@@ -1,51 +0,0 @@
1187-<!DOCTYPE html>
1188-<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
1189-<head>
1190-  <meta charset="utf-8" />
1191-  <meta name="generator" content="pandoc" />
1192-  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
1193-$for(author-meta)$
1194-  <meta name="author" content="$author-meta$" />
1195-$endfor$
1196-$if(date-meta)$
1197-  <meta name="dcterms.date" content="$date-meta$" />
1198-$endif$
1199-$if(keywords)$
1200-  <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
1201-$endif$
1202-$if(description-meta)$
1203-  <meta name="description" content="$description-meta$" />
1204-$endif$
1205-  <title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
1206-  <style>
1207-    $styles.html()$
1208-  </style>
1209-$for(css)$
1210-  <link rel="stylesheet" href="$css$" />
1211-$endfor$
1212-$if(math)$
1213-  $math$
1214-$endif$
1215-  <!--[if lt IE 9]>
1216-    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
1217-  <![endif]-->
1218-$for(header-includes)$
1219-  $header-includes$
1220-$endfor$
1221-</head>
1222-<body>
1223-</header>
1224-$if(toc)$
1225-<nav id="$idprefix$TOC" role="doc-toc">
1226-$if(toc-title)$
1227-<h2 id="$idprefix$toc-title">$toc-title$</h2>
1228-$endif$
1229-$table-of-contents$
1230-</nav>
1231-$endif$
1232-$body$
1233-$for(include-after)$
1234-$include-after$
1235-$endfor$
1236-</body>
1237-</html>
1238diff --git a/go.mod b/go.mod
1239new file mode 100644
1240index 0000000000000000000000000000000000000000..298fa2b78ae0fa9364584e43746e23dd1ddd7f4a
1241--- /dev/null
1242+++ b/go.mod
1243@@ -0,0 +1,5 @@
1244+module git.sr.ht/~gabrielgio/jnfilter
1245+
1246+go 1.21.7
1247+
1248+require github.com/beevik/etree v1.3.0
1249diff --git a/go.sum b/go.sum
1250new file mode 100644
1251index 0000000000000000000000000000000000000000..999dbadd7a37dec8bf5043a8c3502e20d7d92194
1252--- /dev/null
1253+++ b/go.sum
1254@@ -0,0 +1,2 @@
1255+github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
1256+github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
1257diff --git a/jnfilter/__init__.py b/jnfilter/__init__.py
1258deleted file mode 100644
1259index 80b5e027ea91d9d1881e51989cbf5933f261ef84..0000000000000000000000000000000000000000
1260--- a/jnfilter/__init__.py
1261+++ /dev/null
1262@@ -1,102 +0,0 @@
1263-import re
1264-import httpx
1265-
1266-from functools import reduce
1267-from typing import List, Iterator, Union
1268-from xml.etree.ElementTree import ElementTree, fromstring, tostring, register_namespace
1269-
1270-from flask import Flask, Response, request
1271-
1272-app =  Flask(__name__)
1273-
1274-URL = "https://api.jovemnerd.com.br/feed-nerdcast/"
1275-
1276-RegexCollection = {
1277-    "nerdcast": "NerdCast [0-9]+[a-c]* -",
1278-    "empreendedor": "Empreendedor [0-9]+ -",
1279-    "mamicas": "Caneca de Mamicas [0-9]+ -",
1280-    "english": "Speak English [0-9]+ -",
1281-    "nerdcash": "NerdCash [0-9]+ -",
1282-    "bunker": "Lá do Bunker  [0-9]+ -",
1283-    "tech": "NerdTech [0-9]+ -",
1284-    "genera": "Generacast [0-9]+ -",
1285-}
1286-
1287-ATOM =  "http://www.w3.org/2005/Atom"
1288-ITUNES =  "http://www.itunes.com/dtds/podcast-1.0.dtd"
1289-GOOGLEPLAY = "http://www.google.com/schemas/play-podcasts/1.0"
1290-
1291-register_namespace("googleplay", GOOGLEPLAY)
1292-register_namespace("itunes", ITUNES)
1293-register_namespace("atom", ATOM)
1294-
1295-
1296-
1297-def match(title: str, series: List[str]) -> bool:
1298-    def _match(s):
1299-        return re.match(RegexCollection[s], title) is not None
1300-
1301-    return reduce(lambda x, y: x or _match(y), series, False)
1302-
1303-
1304-def filter_xml(xml_str: str, series: List[str], tag: Union[bool, None] = False) -> str:
1305-    tree = ElementTree(fromstring(xml_str))
1306-    tree_root = tree.getroot()
1307-    for channel in tree_root.findall("./channel"):
1308-
1309-        if tag:
1310-            tag = f' [{",".join(series)}]'.upper()
1311-            channel.find("title").text += tag
1312-            channel.find("description").text += tag
1313-            channel.find("link").text += f"?{tag}"
1314-            channel.find(f"{{{ITUNES}}}author").text += tag
1315-            channel.find(f"{{{GOOGLEPLAY}}}author").text += tag
1316-            channel.find(f"{{{ITUNES}}}subtitle").text += tag
1317-            channel.find(f"{{{ITUNES}}}summary").text += tag
1318-
1319-        for item in channel.findall("item"):
1320-            title = item.find("title").text
1321-            if not match(title, series):
1322-                channel.remove(item)
1323-
1324-    return tostring(tree_root, encoding='utf8', method='xml')
1325-
1326-
1327-def filter_titles_xml(xml_str) -> Iterator[str]:
1328-    tree = ElementTree(fromstring(xml_str))
1329-    tree_root = tree.getroot()
1330-    for item in tree_root.findall("./channel/item"):
1331-        yield item.find("title").text
1332-
1333-
1334-def load_and_filter(series: str, tag: Union[bool, None] = False) -> str:
1335-    series = series or 'nerdcast'
1336-    series = series.split(',')
1337-    with httpx.Client() as client:
1338-        response =client.get(URL)
1339-        xml_str = response.content
1340-        return filter_xml(xml_str, series, tag)
1341-
1342-
1343-def load_titles() -> Iterator[str]:
1344-    with httpx.Client() as client:
1345-        response = client.get(URL)
1346-        xml_str = response.content
1347-        return filter_titles_xml(xml_str)
1348-
1349-@app.route("/", methods=['GET', 'HEAD'])
1350-def root(q: str = '', tag: Union[bool, None] = False):
1351-    q = request.args.get("q", "")
1352-    tag = request.args.get("tag", False)
1353-    return load_and_filter(q, tag), 200,  {'Content-Type': 'application/xml'}
1354-
1355-
1356-@app.route("/titles", methods=['GET'])
1357-def titles():
1358-    titles = load_titles()
1359-    return "\n".join(titles)
1360-
1361-
1362-@app.route("/series", methods=['GET'])
1363-def series():
1364-    return [i[0] for i in RegexCollection.items()]
1365diff --git a/jnfilter/__main__.py b/jnfilter/__main__.py
1366deleted file mode 100644
1367index ff2b876b8a6e8561b7cf4bcd07153c57cf6527da..0000000000000000000000000000000000000000
1368--- a/jnfilter/__main__.py
1369+++ /dev/null
1370@@ -1,4 +0,0 @@
1371-from . import run
1372-
1373-if __name__ == '__main__':
1374-    run()
1375diff --git a/main.go b/main.go
1376new file mode 100644
1377index 0000000000000000000000000000000000000000..945b30bb390b009e354cc89194b378ece5dd2be5
1378--- /dev/null
1379+++ b/main.go
1380@@ -0,0 +1,188 @@
1381+package main
1382+
1383+import (
1384+	"context"
1385+	"errors"
1386+	"flag"
1387+	"fmt"
1388+	"io"
1389+	"net/http"
1390+	"regexp"
1391+	"strings"
1392+
1393+	"github.com/beevik/etree"
1394+)
1395+
1396+type ErrorRequestHandler func(w http.ResponseWriter, r *http.Request) error
1397+
1398+var RegexCollection = map[string]string{
1399+	"nerdcast":     "NerdCast [0-9]+[a-c]* -",
1400+	"empreendedor": "Empreendedor [0-9]+ -",
1401+	"mamicas":      "Caneca de Mamicas [0-9]+ -",
1402+	"english":      "Speak English [0-9]+ -",
1403+	"nerdcash":     "NerdCash [0-9]+ -",
1404+	"bunker":       "Lá do Bunker [0-9]+ -",
1405+	"tech":         "NerdTech [0-9]+ -",
1406+	"genera":       "Generacast [0-9]+ -",
1407+}
1408+
1409+const (
1410+	FeedUrl = "https://api.jovemnerd.com.br/feed-nerdcast/"
1411+)
1412+
1413+func getSeries(r *http.Request) []string {
1414+	query := r.URL.Query().Get("q")
1415+
1416+	var series []string
1417+
1418+	for _, q := range strings.Split(query, ",") {
1419+		if _, ok := RegexCollection[q]; ok {
1420+			series = append(series, q)
1421+		}
1422+	}
1423+
1424+	if len(series) > 0 {
1425+		return series
1426+	}
1427+
1428+	return []string{"nerdcast"}
1429+}
1430+
1431+func match(title string, series []string) bool {
1432+	for _, s := range series {
1433+		if ok, err := regexp.MatchString(RegexCollection[s], title); err == nil && ok {
1434+			return true
1435+		}
1436+	}
1437+
1438+	return false
1439+}
1440+
1441+func fetchXML(_ context.Context) ([]byte, error) {
1442+	res, err := http.Get(FeedUrl)
1443+	if err != nil {
1444+		return nil, err
1445+	}
1446+	defer res.Body.Close()
1447+
1448+	if res.StatusCode == http.StatusOK {
1449+		return io.ReadAll(res.Body)
1450+	}
1451+
1452+	return nil, errors.New("Invalid http code")
1453+}
1454+
1455+func appendTag(tag *etree.Element, ap string) {
1456+	text := tag.Text()
1457+	tag.SetText(text + ap)
1458+}
1459+
1460+func filterBySeries(series []string, xml []byte, temper bool) ([]byte, error) {
1461+	doc := etree.NewDocument()
1462+	err := doc.ReadFromBytes(xml)
1463+	if err != nil {
1464+		return nil, err
1465+	}
1466+
1467+	channel := doc.FindElement("//channel")
1468+
1469+	if temper {
1470+		tmp := strings.ToUpper(strings.Join(series, ","))
1471+		tmp = fmt.Sprintf(" [%s]", tmp)
1472+		appendTag(channel.FindElement("title"), tmp)
1473+		appendTag(channel.FindElement("description"), tmp)
1474+		appendTag(channel.FindElement("link"), "?"+tmp)
1475+		appendTag(channel.FindElement("author[namespace-prefix()='itunes']"), tmp)
1476+		appendTag(channel.FindElement("subtitle[namespace-prefix()='itunes']"), tmp)
1477+		appendTag(channel.FindElement("summary[namespace-prefix()='itunes']"), tmp)
1478+		appendTag(channel.FindElement("author[namespace-prefix()='googleplay']"), tmp)
1479+
1480+	}
1481+
1482+	for _, tag := range channel.FindElements("item") {
1483+		title := tag.FindElement("title").Text()
1484+		if !match(title, series) {
1485+			channel.RemoveChild(tag)
1486+		}
1487+	}
1488+
1489+	return doc.WriteToBytes()
1490+}
1491+
1492+func wrap(next ErrorRequestHandler) http.HandlerFunc {
1493+	return func(w http.ResponseWriter, r *http.Request) {
1494+		if err := next(w, r); err != nil {
1495+			w.WriteHeader(http.StatusInternalServerError)
1496+		}
1497+	}
1498+}
1499+
1500+func titles(w http.ResponseWriter, r *http.Request) error {
1501+	xml, err := fetchXML(r.Context())
1502+	if err != nil {
1503+		return err
1504+	}
1505+
1506+	doc := etree.NewDocument()
1507+	err = doc.ReadFromBytes(xml)
1508+	if err != nil {
1509+		return err
1510+	}
1511+
1512+	series := getSeries(r)
1513+
1514+	els := doc.FindElements("//channel/item")
1515+	for _, e := range els {
1516+		txt := e.FindElement("title").Text() + "\n"
1517+		if match(txt, series) {
1518+			_, err = w.Write([]byte(txt))
1519+			if err != nil {
1520+				return err
1521+			}
1522+		}
1523+	}
1524+
1525+	return nil
1526+}
1527+
1528+func podcast(w http.ResponseWriter, r *http.Request) error {
1529+	xml, err := fetchXML(r.Context())
1530+	if err != nil {
1531+		return err
1532+	}
1533+
1534+	series := getSeries(r)
1535+	filterdXML, err := filterBySeries(series, xml, true)
1536+	if err != nil {
1537+		return err
1538+	}
1539+
1540+	_, err = w.Write(filterdXML)
1541+	if err != nil {
1542+		return err
1543+	}
1544+
1545+	return nil
1546+}
1547+
1548+func main() {
1549+	var (
1550+		addr = flag.String("addr", ":8080", "Server address")
1551+	)
1552+
1553+	flag.Parse()
1554+
1555+	mux := http.NewServeMux()
1556+	mux.HandleFunc("/titles", wrap(titles))
1557+	mux.HandleFunc("/", wrap(podcast))
1558+
1559+	server := http.Server{
1560+		Handler: mux,
1561+		Addr:    *addr,
1562+	}
1563+
1564+	err := server.ListenAndServe()
1565+	if err != nil {
1566+		fmt.Printf("Server error: %s", err.Error())
1567+	}
1568+}
1569diff --git a/setup.py b/setup.py
1570deleted file mode 100644
1571index 7692d27d74355ce026d0fc38d5e9febdd85f2026..0000000000000000000000000000000000000000
1572--- a/setup.py
1573+++ /dev/null
1574@@ -1,23 +0,0 @@
1575-from setuptools import setup
1576-
1577-requirements = [
1578-    'httpx==0.21.1',
1579-    'flask==2.2.2',
1580-]
1581-
1582-
1583-setup(name='jnfilter',
1584-    version='0.3.0',
1585-    description='A FastAPI server to filter Nercast podcast feed',
1586-    url='https://git.sr.ht/~gabrielgio/jnfilter',
1587-    author='Gabriel Arakaki Giovanini',
1588-    author_email='mail@gabrielgio.me',
1589-    license='MIT',
1590-    packages=['jnfilter'],
1591-    entry_points={
1592-        'console_scripts': [
1593-            'jnfilterd=jnfilter',
1594-        ]
1595-    },
1596-    install_requires=requirements,
1597-    zip_safe=False)
1598diff --git a/test/dev-requirements.txt b/test/dev-requirements.txt
1599deleted file mode 100644
1600index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
1601Binary files a/test/dev-requirements.txt and /dev/null differ
1602diff --git a/test/test_main.py b/test/test_main.py
1603deleted file mode 100644
1604index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
1605Binary files a/test/test_main.py and /dev/null differ