Refactored telegram bot server

This commit is contained in:
Pavel Kachalouski
2018-10-21 00:48:48 +02:00
parent 7bfd8ae49f
commit 38c18570ff
24 changed files with 881 additions and 1005 deletions

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="delivery-checker-bot-build [file:/home/nnm/development/telegram-bot1/]" external.linked.project.path="$MODULE_DIR$/../../delivery-checker-bot/project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="SUB:DOLLAR2a7cf0a3aa9f47ee5733.`deliverySUB:DOLLARminuscheckerSUB:DOLLARminusbot`, SUB:DOLLAR2a7cf0a3aa9f47ee5733.`telegramSUB:DOLLARminusbot`, _root_.sbt.Keys._, _root_.sbt.ScriptedPlugin.autoImport._, _root_.sbtassembly.AssemblyPlugin.autoImport._, _root_.sbt._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.ScriptedPlugin, _root_.sbt.plugins.SbtPlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.sbtassembly.AssemblyPlugin, _root_.scala.xml.{TopScope=&gt;SUB:DOLLARscope}" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, file:/home/nnm/.sbt/preloaded/|maven|local-preloaded, /home/nnm/.ivy2/cache|ivy|Local cache" type="SBT_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/../../delivery-checker-bot/project/target/idea-classes" />
<output-test url="file://$MODULE_DIR$/../../delivery-checker-bot/project/target/idea-test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../delivery-checker-bot/project">
<excludeFolder url="file://$MODULE_DIR$/../../delivery-checker-bot/project/project/target" />
<excludeFolder url="file://$MODULE_DIR$/../../delivery-checker-bot/project/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="telegram-bot1-build" />
<orderEntry type="module-library">
<library name="sbt: sbt-and-plugins">
<CLASSES>
<root url="jar://$USER_HOME$/.ivy2/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/javax.enterprise/cdi-api/jars/cdi-api-1.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/javax.inject/javax.inject/jars/javax.inject-1.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.4.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-artifact/jars/maven-artifact-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-model/jars/maven-model-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-plugin-api/jars/maven-plugin-api-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-classworlds/bundles/plexus-classworlds-2.5.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-component-annotations/jars/plexus-component-annotations-1.5.5.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-utils/jars/plexus-utils-3.0.22.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.inject/eclipse-plugins/org.eclipse.sisu.inject-0.3.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.plexus/eclipse-plugins/org.eclipse.sisu.plexus-0.3.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-commons/jars/asm-commons-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-tree/jars/asm-tree-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm/jars/asm-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.pantsbuild/jarjar/jars/jarjar-1.6.5.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.1.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/scala_2.12/sbt_1.0/com.eed3si9n/sbt-assembly/jars/sbt-assembly-0.14.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/lib/jline.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/lib/scala-compiler.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/lib/scala-library.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/lib/scala-reflect.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/lib/scala-xml_2.12.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/actions_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/apple-file-events-1.3.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/caffeine-2.5.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/collections_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/command_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/compiler-bridge_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/compiler-interface-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/completion_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/config-1.2.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/core-macros_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/disruptor-3.3.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/fastparse-utils_2.12-0.4.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/fastparse_2.12-0.4.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/gigahorse-core_2.12-0.3.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/gigahorse-okhttp_2.12-0.3.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/io_2.12-1.2.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/ipcsocket-1.0.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/ivy-2.3.0-sbt-b18f59ea3bc914a297bb6f1a4f7fb0ace399e310.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/jawn-parser_2.12-0.10.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/jline-2.14.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/jna-4.5.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/jna-platform-4.5.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/jsch-0.1.54.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/launcher-interface-1.0.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/lenses_2.12-0.4.12.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/librarymanagement-core_2.12-1.2.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/librarymanagement-ivy_2.12-1.2.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/log4j-api-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/log4j-core-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/log4j-slf4j-impl-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/logic_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/main-settings_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/main_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/okhttp-3.7.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/okhttp-urlconnection-3.7.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/okio-1.12.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/protobuf-java-3.3.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/protocol_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/reactive-streams-1.0.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/run_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sbinary_2.12-0.5.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sbt-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scala-parser-combinators_2.12-1.0.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scala-reflect-2.12.7.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scala-xml_2.12-1.0.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scalacache-caffeine_2.12-0.20.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scalacache-core_2.12-0.20.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scalapb-runtime_2.12-0.6.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scripted-plugin_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/scripted-sbt-redux_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/shaded-scalajson_2.12-1.0.0-M4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sjson-new-core_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sjson-new-murmurhash_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sjson-new-scalajson_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/slf4j-api-1.7.25.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/sourcecode_2.12-0.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/ssl-config-core_2.12-0.2.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/task-system_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/tasks_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/template-resolver-0.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/test-agent-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/test-interface-1.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/testing_2.12-1.2.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-cache_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-control_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-interface-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-logging_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-position_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-relation_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-scripted_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/util-tracking_2.12-1.2.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-apiinfo_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-classfile_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-classpath_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-compile-core_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-compile_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-core_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-ivy-integration_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc-persist_2.12-1.2.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.7/org.scala-sbt/sbt/1.2.6/zinc_2.12-1.2.4.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
<component name="SbtModule">
<option name="buildForId" value="delivery-checker-bot" />
<option name="buildForURI" value="file:$MODULE_DIR$/../../" />
<option name="imports" value="SUB:DOLLAR2a7cf0a3aa9f47ee5733.`deliverySUB:DOLLARminuscheckerSUB:DOLLARminusbot`, SUB:DOLLAR2a7cf0a3aa9f47ee5733.`telegramSUB:DOLLARminusbot`, _root_.sbt.Keys._, _root_.sbt.ScriptedPlugin.autoImport._, _root_.sbtassembly.AssemblyPlugin.autoImport._, _root_.sbt._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.ScriptedPlugin, _root_.sbt.plugins.SbtPlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.sbtassembly.AssemblyPlugin, _root_.scala.xml.{TopScope=&gt;SUB:DOLLARscope}" />
</component>
</module>

44
.idea/modules/delivery-checker-bot.iml generated Normal file
View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="delivery-checker-bot [file:/home/nnm/development/telegram-bot1/]" external.linked.project.path="$MODULE_DIR$/../../delivery-checker-bot" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../delivery-checker-bot/target/scala-2.12/classes" />
<output-test url="file://$MODULE_DIR$/../../delivery-checker-bot/target/scala-2.12/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../delivery-checker-bot">
<sourceFolder url="file://$MODULE_DIR$/../../delivery-checker-bot/target/scala-2.12/src_managed/main" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../delivery-checker-bot/target/scala-2.12/src_managed/test" isTestSource="true" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/../../delivery-checker-bot/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="sbt: com.chuusai:shapeless_2.12:2.3.3:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:machinist_2.12:0.6.5:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-macros_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-kernel_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-core_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.spire-math:jawn-parser_2.12:0.13.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-parser-combinators_2.12:1.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-parser_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-numbers_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-jawn_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-generic_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-core_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-stream_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-protobuf_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-parsing_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http-core_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor-typed_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:ssl-config-core_2.12:0.2.4:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:config:1.3.3:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:macro-compat_2.12:1.1.1:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalactic:scalactic_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scala-lang.modules:scala-xml_2.12:1.0.6:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-java8-compat_2.12:0.8.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.reactivestreams:reactive-streams:1.0.2:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-library:2.12.7:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-reflect:2.12.7:jar" level="project" />
</component>
</module>

View File

@@ -1,124 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="root-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="SUB:DOLLAR4d5286406012615efe32.`root`, _root_.sbt.Keys._, _root_.sbt._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.sbt.plugins.Giter8TemplatePlugin, _root_.sbtassembly.AssemblyPlugin, _root_.sbtassembly.AssemblyPlugin.autoImport._, _root_.scala.xml.{TopScope=&gt;SUB:DOLLARscope}" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, file:/home/nnm/.sbt/preloaded/|maven|local-preloaded, /home/nnm/.ivy2/cache|ivy|Local cache" type="SBT_MODULE" version="4">
<component name="NewModuleRootManager">
<output url="file://$MODULE_DIR$/../../project/target/idea-classes" />
<output-test url="file://$MODULE_DIR$/../../project/target/idea-test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../project">
<sourceFolder url="file://$MODULE_DIR$/../../project" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/../../project/project/target" />
<excludeFolder url="file://$MODULE_DIR$/../../project/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library name="sbt: sbt-and-plugins">
<CLASSES>
<root url="jar://$USER_HOME$/.ivy2/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/javax.enterprise/cdi-api/jars/cdi-api-1.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/javax.inject/javax.inject/jars/javax.inject-1.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.4.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-artifact/jars/maven-artifact-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-model/jars/maven-model-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-plugin-api/jars/maven-plugin-api-3.3.9.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-classworlds/bundles/plexus-classworlds-2.5.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-component-annotations/jars/plexus-component-annotations-1.5.5.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-utils/jars/plexus-utils-3.0.22.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.inject/eclipse-plugins/org.eclipse.sisu.inject-0.3.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.plexus/eclipse-plugins/org.eclipse.sisu.plexus-0.3.2.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-commons/jars/asm-commons-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-tree/jars/asm-tree-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm/jars/asm-6.0.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.pantsbuild/jarjar/jars/jarjar-1.6.5.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.1.jar!/" />
<root url="jar://$USER_HOME$/.ivy2/cache/scala_2.12/sbt_1.0/com.eed3si9n/sbt-assembly/jars/sbt-assembly-0.14.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/lib/jline.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/lib/scala-compiler.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/lib/scala-library.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/lib/scala-reflect.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/lib/scala-xml_2.12.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/actions_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/apple-file-events-1.3.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/caffeine-2.5.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/collections_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/command_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/compiler-interface-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/completion_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/config-1.2.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/core-macros_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/disruptor-3.3.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/fastparse-utils_2.12-0.4.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/fastparse_2.12-0.4.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/gigahorse-core_2.12-0.3.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/gigahorse-okhttp_2.12-0.3.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/io_2.12-1.1.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/ipcsocket-1.0.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/ivy-2.3.0-sbt-b18f59ea3bc914a297bb6f1a4f7fb0ace399e310.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/jawn-parser_2.12-0.10.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/jline-2.14.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/jna-4.5.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/jna-platform-4.5.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/jsch-0.1.54.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/launcher-interface-1.0.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/lenses_2.12-0.4.12.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/librarymanagement-core_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/librarymanagement-ivy_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/log4j-api-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/log4j-core-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/log4j-slf4j-impl-2.8.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/logic_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/main-settings_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/main_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/okhttp-3.7.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/okhttp-urlconnection-3.7.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/okio-1.12.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/protobuf-java-3.3.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/protocol_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/reactive-streams-1.0.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/run_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sbinary_2.12-0.4.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sbt-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scala-parser-combinators_2.12-1.0.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scala-reflect-2.12.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scala-xml_2.12-1.0.6.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scalacache-caffeine_2.12-0.20.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scalacache-core_2.12-0.20.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/scalapb-runtime_2.12-0.6.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/shaded-scalajson_2.12-1.0.0-M4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sjson-new-core_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sjson-new-murmurhash_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sjson-new-scalajson_2.12-0.8.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/slf4j-api-1.7.25.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/sourcecode_2.12-0.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/ssl-config-core_2.12-0.2.2.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/task-system_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/tasks_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/template-resolver-0.1.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/test-agent-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/test-interface-1.0.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/testing_2.12-1.1.4.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-cache_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-control_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-interface-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-logging_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-position_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-relation_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/util-tracking_2.12-1.1.3.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-apiinfo_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-classfile_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-classpath_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-compile-core_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-compile_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-core_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-ivy-integration_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc-persist_2.12-1.1.5.jar!/" />
<root url="jar://$USER_HOME$/.sbt/boot/scala-2.12.4/org.scala-sbt/sbt/1.1.4/zinc_2.12-1.1.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>
</component>
</module>

79
.idea/modules/root.iml generated
View File

@@ -1,79 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="root" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../target/scala-2.12/classes" />
<output-test url="file://$MODULE_DIR$/../../target/scala-2.12/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../src/main/scala" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../target/scala-2.12/src_managed/main" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../target/scala-2.12/src_managed/test" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/../../target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="sbt: com.google.code.gson:gson:2.8.1:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor-typed_2.12:2.5.12:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:macro-compat_2.12:1.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:machinist_2.12:0.6.2:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-macros_2.12:1.0.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-kernel_2.12:1.0.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-core_2.12:1.0.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.spire-math:jawn-parser_2.12:0.11.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-reflect:2.12.6:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-parser_2.12:0.9.3:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-numbers_2.12:0.9.3:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-jawn_2.12:0.9.3:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-generic_2.12:0.9.3:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-core_2.12:0.9.3:jar" level="project" />
<orderEntry type="library" name="sbt: com.chuusai:shapeless_2.12:2.3.3:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:config:1.3.2:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:ssl-config-core_2.12:0.2.3:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor_2.12:2.5.12:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http-core_2.12:10.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http_2.12:10.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-parsing_2.12:10.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-protobuf_2.12:2.5.12:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-stream_2.12:2.5.12:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.netty:netty-reactive-streams:1.0.8:jar" level="project" />
<orderEntry type="library" name="sbt: com.vk.api:sdk:0.5.12:jar" level="project" />
<orderEntry type="library" name="sbt: commons-codec:commons-codec:1.9:jar" level="project" />
<orderEntry type="library" name="sbt: commons-io:commons-io:2.5:jar" level="project" />
<orderEntry type="library" name="sbt: commons-logging:commons-logging:1.2:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-buffer:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-codec:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-codec-http:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-common:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-handler:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-transport:4.0.48.Final:jar" level="project" />
<orderEntry type="library" name="sbt: io.netty:netty-transport-native-epoll:4.0.48.Final:linux-x86_64:jar" level="project" />
<orderEntry type="library" name="sbt: org.apache.commons:commons-collections4:4.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.apache.commons:commons-lang3:3.6:jar" level="project" />
<orderEntry type="library" name="sbt: org.apache.httpcomponents:httpclient:4.5.3:jar" level="project" />
<orderEntry type="library" name="sbt: org.apache.httpcomponents:httpcore:4.4.6:jar" level="project" />
<orderEntry type="library" name="sbt: org.apache.httpcomponents:httpmime:4.5.3:jar" level="project" />
<orderEntry type="library" name="sbt: org.asynchttpclient:async-http-client:2.0.33:jar" level="project" />
<orderEntry type="library" name="sbt: org.asynchttpclient:async-http-client-netty-utils:2.0.33:jar" level="project" />
<orderEntry type="library" name="sbt: org.asynchttpclient:netty-codec-dns:2.0.33:jar" level="project" />
<orderEntry type="library" name="sbt: org.asynchttpclient:netty-resolver:2.0.33:jar" level="project" />
<orderEntry type="library" name="sbt: org.asynchttpclient:netty-resolver-dns:2.0.33:jar" level="project" />
<orderEntry type="library" name="sbt: org.reactivestreams:reactive-streams:1.0.2:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-library:2.12.6:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-java8-compat_2.12:0.8.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-parser-combinators_2.12:1.1.0:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scala-lang.modules:scala-xml_2.12:1.0.6:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalactic:scalactic_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" name="sbt: org.slf4j:slf4j-api:1.7.25:jar" level="project" />
<orderEntry type="module-library">
<library name="sbt: unmanaged-sources-and-docs">
<CLASSES />
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.ivy2/cache/io.netty/netty-transport-native-epoll/srcs/netty-transport-native-epoll-4.0.48.Final-sources.jar!/" />
</SOURCES>
</library>
</orderEntry>
</component>
</module>

47
.idea/modules/telegram-bot.iml generated Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="telegram-bot [file:/home/nnm/development/telegram-bot1/]" external.linked.project.path="$MODULE_DIR$/../../telegram-bot" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../telegram-bot/target/scala-2.12/classes" />
<output-test url="file://$MODULE_DIR$/../../telegram-bot/target/scala-2.12/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../../telegram-bot">
<sourceFolder url="file://$MODULE_DIR$/../../telegram-bot/src/main/scala" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/../../telegram-bot/target/scala-2.12/src_managed/main" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../telegram-bot/target/scala-2.12/src_managed/test" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../telegram-bot/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/../../telegram-bot/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="sbt: com.chuusai:shapeless_2.12:2.3.3:jar" level="project" />
<orderEntry type="library" name="sbt: de.heikoseeberger:akka-http-circe_2.12:1.22.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:machinist_2.12:0.6.5:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-macros_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-kernel_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:cats-core_2.12:1.4.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.spire-math:jawn-parser_2.12:0.13.0:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-parser-combinators_2.12:1.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-parser_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-numbers_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-jawn_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-generic_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: io.circe:circe-core_2.12:0.10.0:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-stream_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-protobuf_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-parsing_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-http-core_2.12:10.1.5:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe.akka:akka-actor-typed_2.12:2.5.17:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:ssl-config-core_2.12:0.2.4:jar" level="project" />
<orderEntry type="library" name="sbt: com.typesafe:config:1.3.3:jar" level="project" />
<orderEntry type="library" name="sbt: org.reactivestreams:reactive-streams:1.0.2:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang.modules:scala-java8-compat_2.12:0.8.0:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scala-lang.modules:scala-xml_2.12:1.0.6:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalactic:scalactic_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" scope="TEST" name="sbt: org.scalatest:scalatest_2.12:3.0.5:jar" level="project" />
<orderEntry type="library" name="sbt: org.typelevel:macro-compat_2.12:1.1.1:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-library:2.12.7:jar" level="project" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-reflect:2.12.7:jar" level="project" />
</component>
</module>

16
.idea/modules/telegram-bot1.iml generated Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="telegram-bot1 [file:/home/nnm/development/telegram-bot1/]" external.linked.project.path="$MODULE_DIR$/../.." external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../target/scala-2.12/classes" />
<output-test url="file://$MODULE_DIR$/../../target/scala-2.12/test-classes" />
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<sourceFolder url="file://$MODULE_DIR$/../../target/scala-2.12/src_managed/main" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/../../target/scala-2.12/src_managed/test" isTestSource="true" generated="true" />
<excludeFolder url="file://$MODULE_DIR$/../../target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="sbt: org.scala-lang:scala-library:2.12.7:jar" level="project" />
</component>
</module>

View File

@@ -1,21 +1,37 @@
import Dependencies._
lazy val root = (project in file(".")).
settings(
inThisBuild(List(
organization := "com.example",
scalaVersion := "2.12.6",
version := "0.1.0-SNAPSHOT",
mainClass := Some("eu.xeppaka.bot1.TelegramBotServer")
)),
name := "telegram-bot1",
lazy val commonSettings = Seq(
organization := "com.example",
scalaVersion := "2.12.7",
version := "0.1.0-SNAPSHOT",
mainClass := Some("eu.xeppaka.bot.TelegramBotServer")
)
inThisBuild(commonSettings)
lazy val `telegram-bot` = (project in file("telegram-bot"))
.settings(
libraryDependencies ++= Seq(
scalaTest % Test,
akka,
akkaTyped,
akkaHttp,
akkaStream,
circleCore,
circleGeneric,
circleParser,
circeAkkaHttp
)
)
lazy val `delivery-checker-bot` = (project in file("delivery-checker-bot"))
.settings(
libraryDependencies ++= Seq(
scalaTest % Test,
akka,
akkaTyped,
akkaHttp,
akkaStream,
vkapi,
circleCore,
circleGeneric,
circleParser

View File

@@ -1,13 +1,14 @@
import sbt._
object Dependencies {
lazy val akka = "com.typesafe.akka" %% "akka-actor" % "2.5.17"
lazy val akkaTyped = "com.typesafe.akka" %% "akka-actor-typed" % "2.5.17"
lazy val akkaStream = "com.typesafe.akka" %% "akka-stream" % "2.5.17"
lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.1.5"
//lazy val vkapi = "com.vk.api" % "sdk" % "0.5.12"
lazy val circleCore = "io.circe" %% "circe-core" % "0.10.0"
lazy val circleGeneric = "io.circe" %% "circe-generic" % "0.10.0"
lazy val circleParser = "io.circe" %% "circe-parser" % "0.10.0"
lazy val circeAkkaHttp = "de.heikoseeberger" %% "akka-http-circe" % "1.22.0"
lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5"
lazy val akka = "com.typesafe.akka" %% "akka-actor" % "2.5.12"
lazy val akkaTyped = "com.typesafe.akka" %% "akka-actor-typed" % "2.5.12"
lazy val akkaHttp = "com.typesafe.akka" %% "akka-http" % "10.1.1"
lazy val akkaStream = "com.typesafe.akka" %% "akka-stream" % "2.5.12"
lazy val vkapi = "com.vk.api" % "sdk" % "0.5.12"
lazy val circleCore = "io.circe" %% "circe-core" % "0.9.3"
lazy val circleGeneric = "io.circe" %% "circe-generic" % "0.9.3"
lazy val circleParser = "io.circe" %% "circe-parser" % "0.9.3"
}

View File

@@ -1 +1 @@
sbt.version=1.1.4
sbt.version=1.2.6

View File

@@ -1,3 +0,0 @@
akka {
loglevel = "INFO"
}

Binary file not shown.

View File

@@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIJAKLMfxW4KRHuMA0GCSqGSIb3DQEBCwUAME8xCzAJBgNV
BAYTAkNaMQ8wDQYDVQQHDAZQcmFndWUxGjAYBgNVBAoMEVBhdmVsIEthY2hhbG91
c2tpMRMwEQYDVQQDDAp4ZXBwYWthLmV1MB4XDTE4MDUxMTE4MjEzOVoXDTI4MDUw
ODE4MjEzOVowTzELMAkGA1UEBhMCQ1oxDzANBgNVBAcMBlByYWd1ZTEaMBgGA1UE
CgwRUGF2ZWwgS2FjaGFsb3Vza2kxEzARBgNVBAMMCnhlcHBha2EuZXUwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWwewslKtWJ7yeYrxDuoh5PS7y5/C/
0NT6tsAAsh3ZVCqdeNvYj56n8jVob/jJ9EYMqKj7dXLAMopDhtuwdDN/KhW9QVkK
zATE1wNxuV3aBVUTJuHHadUYQa7pVevvssAIa1XQ6NvU0pkwdDApylOj1TkA9MFl
ZWHWlF0dgrVyGjFxDoWdjm2aLCdRpZCr0giTOfZ5E+OJNALTHcuJO+PRKdEreO1Y
VAlT2Sk26f8/iG63C2/t7xWTyJKOjFPxwq3+dkNfJ1AXZ4I7aFDgP7BKogvooYuC
BItqog+IRUOoK9Yj24KCUxD+gaI5+tv0j1ov5d0ZAqqaiSql96s2/jyZAgMBAAGj
UDBOMB0GA1UdDgQWBBRkOXFj0c0jNdM1nJMRGr0EvfeMuTAfBgNVHSMEGDAWgBRk
OXFj0c0jNdM1nJMRGr0EvfeMuTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQBtXUOfnKdp1GY5gATTrPdr2s7FyiJvVfx/yeNNNR8ZnZcLjBMulEvXsfNi
AL1cEctnSDyT5z2el75nTdAgTFcBZQLsVk9/4ZwBRnfunFqfU5e5X9X9z//yt4Gy
Zq9BrMddQE+qwnOclcTDCc0GnyqKbaPiyYFcaXKhdrsflvoJI9tyLwPgjfXADLDF
JtjC0gGdbgefDweBUMTF0cpZED9q/J2fKXHurub+3QySvUOvphVFP4dBz2WhdoTe
v3lkEVp3I/IUv9qegO0B0o6X+Nnml4/b7HV1PArNceWOA6f57fSL2m6eN6xs4ULJ
kfUMloAr25yvmN/tPwm+8Op5ovot
-----END CERTIFICATE-----

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2015 Heiko Seeberger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.xeppaka.bot1
import akka.http.scaladsl.marshalling.{ Marshaller, ToEntityMarshaller }
import akka.http.scaladsl.model.{ ContentType, ContentTypeRange, HttpEntity }
import akka.http.scaladsl.model.MediaType
import akka.http.scaladsl.model.MediaTypes.`application/json`
import akka.http.scaladsl.unmarshalling.{ FromEntityUnmarshaller, Unmarshaller }
import akka.util.ByteString
import cats.data.NonEmptyList
import cats.syntax.show.toShow
import io.circe.{ Decoder, DecodingFailure, Encoder, Json, Printer, jawn }
import scala.collection.immutable.Seq
/**
* Automatic to and from JSON marshalling/unmarshalling using an in-scope circe protocol.
* The unmarshaller fails fast, throwing the first `Error` encountered.
*
* To use automatic codec derivation, user needs to import `io.circe.generic.auto._`.
*/
object FailFastCirceSupport extends FailFastCirceSupport
/**
* Automatic to and from JSON marshalling/unmarshalling using an in-scope circe protocol.
* The unmarshaller fails fast, throwing the first `Error` encountered.
*
* To use automatic codec derivation import `io.circe.generic.auto._`.
*/
trait FailFastCirceSupport extends BaseCirceSupport with FailFastUnmarshaller
/**
* Automatic to and from JSON marshalling/unmarshalling using an in-scope circe protocol.
* The unmarshaller accumulates all errors in the exception `Errors`.
*
* To use automatic codec derivation, user needs to import `io.circe.generic.auto._`.
*/
object ErrorAccumulatingCirceSupport extends ErrorAccumulatingCirceSupport {
final case class DecodingFailures(failures: NonEmptyList[DecodingFailure]) extends Exception {
override def getMessage = failures.toList.map(_.show).mkString("\n")
}
}
/**
* Automatic to and from JSON marshalling/unmarshalling using an in-scope circe protocol.
* The unmarshaller accumulates all errors in the exception `Errors`.
*
* To use automatic codec derivation import `io.circe.generic.auto._`.
*/
trait ErrorAccumulatingCirceSupport extends BaseCirceSupport with ErrorAccumulatingUnmarshaller
/**
* Automatic to and from JSON marshalling/unmarshalling using an in-scope circe protocol.
*/
trait BaseCirceSupport {
def unmarshallerContentTypes: Seq[ContentTypeRange] =
mediaTypes.map(ContentTypeRange.apply)
def mediaTypes: Seq[MediaType.WithFixedCharset] =
List(`application/json`)
/**
* `Json` => HTTP entity
*
* @return marshaller for JSON value
*/
implicit final def jsonMarshaller(
implicit printer: Printer = Printer.noSpaces
): ToEntityMarshaller[Json] =
Marshaller.oneOf(mediaTypes: _*) { mediaType =>
Marshaller.withFixedContentType(ContentType(mediaType)) { json =>
HttpEntity(mediaType, printer.pretty(json))
}
}
/**
* `A` => HTTP entity
*
* @tparam A type to encode
* @return marshaller for any `A` value
*/
implicit final def marshaller[A: Encoder](
implicit printer: Printer = Printer.noSpaces
): ToEntityMarshaller[A] =
jsonMarshaller(printer).compose(Encoder[A].apply)
/**
* HTTP entity => `Json`
*
* @return unmarshaller for `Json`
*/
implicit final val jsonUnmarshaller: FromEntityUnmarshaller[Json] =
Unmarshaller.byteStringUnmarshaller
.forContentTypes(unmarshallerContentTypes: _*)
.map {
case ByteString.empty => throw Unmarshaller.NoContentException
case data => jawn.parseByteBuffer(data.asByteBuffer).fold(throw _, identity)
}
/**
* HTTP entity => `A`
*
* @tparam A type to decode
* @return unmarshaller for `A`
*/
implicit def unmarshaller[A: Decoder]: FromEntityUnmarshaller[A]
}
/**
* Mix-in this trait to fail on the first error during unmarshalling.
*/
trait FailFastUnmarshaller { this: BaseCirceSupport =>
override implicit final def unmarshaller[A: Decoder]: FromEntityUnmarshaller[A] = {
def decode(json: Json) = Decoder[A].decodeJson(json).fold(throw _, identity)
jsonUnmarshaller.map(decode)
}
}
/**
* Mix-in this trait to accumulate all errors during unmarshalling.
*/
trait ErrorAccumulatingUnmarshaller { this: BaseCirceSupport =>
override implicit final def unmarshaller[A: Decoder]: FromEntityUnmarshaller[A] = {
def decode(json: Json) =
Decoder[A]
.accumulating(json.hcursor)
.fold(failures => throw ErrorAccumulatingCirceSupport.DecodingFailures(failures), identity)
jsonUnmarshaller.map(decode)
}
}

View File

@@ -1,175 +0,0 @@
package eu.xeppaka.bot1
import java.io.InputStream
import java.security.{KeyStore, SecureRandom}
import java.util.UUID
import akka.actor
import akka.actor.Scheduler
import akka.actor.typed.scaladsl.adapter._
import akka.actor.typed.{ActorSystem, DispatcherSelector}
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.{Route, RouteResult}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.{ConnectionContext, Http, HttpExt, HttpsConnectionContext}
import akka.stream.ActorMaterializer
import akka.util.{ByteString, Timeout}
import eu.xeppaka.bot1.actors.UpdateActor
import eu.xeppaka.bot1.actors.UpdateActor.UpdateResponse
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
import scala.collection.immutable
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContextExecutor, Future}
import scala.io.{Source, StdIn}
import scala.util.{Failure, Success}
class TelegramBotServer(botId: String, port: Int, httpsContext: Option[HttpsConnectionContext]) {
import FailFastCirceSupport._
import eu.xeppaka.bot1.TelegramEntities._
import io.circe.generic.auto._
private val botUri = BotUri(botId)
private implicit val updateSystem: ActorSystem[UpdateActor.UpdateCommand] = ActorSystem(UpdateActor.behavior, "telegram-bot")
private implicit val actorSystem: actor.ActorSystem = updateSystem.toUntyped
private implicit val materializer: ActorMaterializer = ActorMaterializer()
private implicit val executionContext: ExecutionContextExecutor = updateSystem.dispatchers.lookup(DispatcherSelector.default())
private val http: HttpExt = Http()
private val hookId = UUID.randomUUID().toString
private val webhookUri = Uri(s"https://xeppaka.eu:88/$hookId")
private val bindingFuture = http.bindAndHandle(botRoutes(hookId),
"lenovo",
port,
connectionContext = httpsContext.getOrElse(http.defaultClientHttpsContext))
println(s"Webhook path: $webhookUri")
setWebhook()
def stop(): Unit = {
bindingFuture
.flatMap(binding => deleteWebhook().map(_ => binding))
.flatMap(binding => http.shutdownAllConnectionPools().map(_ => binding))
.flatMap(binding => binding.unbind())
.onComplete(_ => updateSystem.terminate())
}
def printRequestMethodAndResponseStatus(req: HttpRequest)(res: RouteResult): Unit = {
println(req)
println(res)
}
def botRoutes(hookId: String): Route = {
path(hookId) {
post {
entity(as[Update]) { _ =>
handleWith(receivedUpdate)
}
}
}
}
private def receivedUpdate(update: Update): Future[HttpResponse] = {
import akka.actor.typed.scaladsl.AskPattern._
implicit val timeout: Timeout = 3.seconds
implicit val scheduler: Scheduler = updateSystem.scheduler
val result: Future[UpdateActor.UpdateResponse] = updateSystem ? (ref => UpdateActor.UpdateReceived(update, ref))
result.andThen {
case Success(response) => sendResponse(response.chatId, response.text)
case Failure(ex) => println("Failed to process message...")
}
result.map(res => HttpResponse()).fallbackTo(Future.successful(HttpResponse()))
}
private def sendResponse(chatId: Long, text: String) = {
import io.circe._, io.circe.generic.auto._, io.circe.syntax._
val sendMessage = SendMessage(chatId, text)
val printer = Printer.noSpaces.copy(dropNullValues = true)
val json = printer.pretty(sendMessage.asJson)
val request = HttpRequest(HttpMethods.POST, uri = botUri.sendMessage, entity = HttpEntity.Strict(ContentTypes.`application/json`, ByteString(json)))
http.singleRequest(request)
}
def getBotInfo: Future[Response[GetMe]] = {
http.singleRequest(HttpRequest(uri = botUri.getMe)).flatMap(Unmarshal(_).to[Response[GetMe]])
}
private def setWebhook(): Future[HttpResponse] = {
print("Setting webhook...")
val urlEntity = HttpEntity.Strict(ContentTypes.`text/plain(UTF-8)`, ByteString(webhookUri.toString()))
val urlPart = Multipart.FormData.BodyPart.Strict("url", urlEntity)
val certificate = ByteString(Source.fromResource("telegram-bot.pem").mkString)
val certificateEntity = HttpEntity.Strict(ContentTypes.`application/octet-stream`, certificate)
val certificatePart = Multipart.FormData.BodyPart.Strict("certificate", certificateEntity, Map("filename" -> "telegram-bot.pem"))
val setWebhookFormData = Multipart.FormData.Strict(immutable.Seq(urlPart, certificatePart))
Marshal(setWebhookFormData)
.to[RequestEntity]
.flatMap(requestEntity => http.singleRequest(HttpRequest(uri = botUri.setWebhook, method = HttpMethods.POST, entity = requestEntity)))
.andThen {
case Success(response) => println(s" ${response.status.value}")
case Failure(exception) => println(s" failed with exception: ${exception.getMessage}")
}
}
private def deleteWebhook(): Future[HttpResponse] = {
print("Deleting webhook...")
http
.singleRequest(HttpRequest(uri = botUri.deleteWebhook, method = HttpMethods.POST))
.andThen {
case Success(response) => println(s" ${response.status.value}")
case Failure(exception) => println(s" failed with exception: ${exception.getMessage}")
}
}
def getWebhookInfo(): Future[Response[WebhookInfo]] = {
http
.singleRequest(HttpRequest(uri = botUri.getWebhookInfo, method = HttpMethods.GET))
.flatMap(Unmarshal(_).to[Response[WebhookInfo]])
}
}
object TelegramBotServer {
private val botId = "570855144:AAEv7b817cuq2JJI9f2kG5B9G3zW1x-btz4"
def apply(port: Int, httpsContext: Option[HttpsConnectionContext]): TelegramBotServer = new TelegramBotServer(botId, port, httpsContext)
def main(args: Array[String]): Unit = {
val httpsContext = createHttpsConnectionContext
val tbs = TelegramBotServer(8443, Some(createHttpsConnectionContext))
StdIn.readLine()
tbs.stop()
}
def createHttpsConnectionContext: HttpsConnectionContext = {
val password: Array[Char] = "".toCharArray // do not store passwords in code, read them from somewhere safe!
val ks: KeyStore = KeyStore.getInstance("PKCS12")
val keystore: InputStream = getClass.getResourceAsStream("/telegram-bot.p12")
require(keystore != null, "Keystore required!")
ks.load(keystore, password)
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(ks, password)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
ConnectionContext.https(sslContext)
}
}

View File

@@ -1,234 +0,0 @@
package eu.xeppaka.bot1
import com.vk.api.sdk.objects.base.Sticker
import com.vk.api.sdk.objects.video.Video
object TelegramEntities {
case class Response[T](ok: Boolean,
description: Option[String] = None,
error_code: Option[Int] = None,
result: T)
case class GetMe(id: Int, is_bot: Boolean, first_name: String, username: String)
case class InlineQuery(id: String,
from: User,
location: Location,
query: String,
offset: String)
case class Location(longitude: Float,
latitude: Float)
case class Update(update_id: Int,
message: Option[Message] = None,
edited_message: Option[Message] = None,
channel_post: Option[Message] = None,
edited_channel_post: Option[Message] = None,
inline_query: Option[InlineQuery] = None,
chosen_inline_result: Option[ChosenInlineResult] = None,
callback_query: Option[CallbackQuery] = None,
shipping_query: Option[ShippingQuery] = None,
pre_checkout_query: Option[PreCheckoutQuery] = None)
case class ChosenInlineResult(result_id: String,
from: User,
location: Option[Location] = None,
inline_message_id: Option[String] = None,
query: String)
case class CallbackQuery(id: String,
from: User,
message: Option[Message] = None,
inline_message_id: Option[String] = None,
chat_instance: String,
data: Option[String] = None,
game_short_name: Option[String] = None)
case class ShippingQuery(id: String,
from: User,
invoice_payload: String,
shipping_address: ShippingAddress)
case class ShippingAddress(country_code: String,
state: String,
city: String,
street_line1: String,
street_line2: String,
post_code: String)
case class PreCheckoutQuery(id: String,
from: User,
currency: String,
total_amount: Int,
invoice_payload: String,
shipping_option_id: Option[String] = None,
order_info: Option[OrderInfo] = None)
case class OrderInfo(name: Option[String] = None,
phone_number: Option[String] = None,
email: Option[String] = None,
shipping_address: Option[ShippingAddress] = None)
case class User(id: Int,
is_bot: Boolean,
first_name: String,
last_name: Option[String] = None,
username: Option[String] = None,
language_code: Option[String] = None)
case class SendMessage(chat_id: Long,
text: String,
parse_mode: Option[String] = None,
disable_web_page_preview: Option[Boolean] = None,
disable_notification: Option[Boolean] = None,
reply_to_message_id: Option[Int] = None,
reply_markup: Option[String] = None)
case class Message(message_id: Int,
from: Option[User] = None,
date: Int,
chat: Chat,
forward_from: Option[User] = None,
forward_from_chat: Option[User] = None,
forward_from_message_id: Option[Int] = None,
forward_signature: Option[String] = None,
forward_date: Option[Int] = None,
reply_to_message: Option[Message] = None,
edit_date: Option[Int] = None,
media_group_id: Option[String] = None,
author_signature: Option[String] = None,
text: Option[String] = None,
entities: Option[Seq[MessageEntity]] = None,
caption_entities: Option[Seq[MessageEntity]] = None,
audio: Option[Audio] = None,
document: Option[Document] = None,
game: Option[Game] = None,
photo: Option[Seq[PhotoSize]] = None,
sticker: Option[Sticker] = None,
video: Option[Video] = None,
voice: Option[Voice] = None,
video_note: Option[VideoNote] = None,
caption: Option[String] = None,
contact: Option[Contact] = None,
location: Option[Location] = None,
venue: Option[Venue] = None,
new_chat_members: Option[Seq[User]] = None,
left_chat_member: Option[Seq[User]] = None,
new_chat_title: Option[String] = None,
new_chat_photo: Option[Seq[PhotoSize]] = None,
delete_chat_photo: Option[Boolean] = None,
group_chat_created: Option[Boolean] = None,
supergroup_chat_created: Option[Boolean] = None,
channel_chat_created: Option[Boolean] = None,
migrate_to_chat_id: Option[Int] = None,
migrate_from_chat_id: Option[Int] = None,
pinned_message: Option[Message] = None,
invoice: Option[Invoice] = None,
successful_payment: Option[SuccessfulPayment] = None,
connected_website: Option[String] = None)
case class MessageEntity(`type`: String,
offset: Int,
length: Int,
url: Option[String] = None,
user: Option[User] = None)
case class Contact(phone_number: String,
first_name: String,
last_name: Option[String] = None,
user_id: Option[Int] = None)
case class Audio(file_id: String,
duration: Int,
performer: Option[String] = None,
title: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None)
case class Document(file_id: String,
thumb: Option[PhotoSize] = None,
file_name: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None)
case class PhotoSize(file_id: String,
width: Int,
height: Int,
file_size: Option[Int] = None)
case class Voice(file_id: String,
duration: Int,
mime_type: Option[String] = None,
file_size: Option[Int] = None)
case class VideoNote(file_id: String,
length: Int,
duration: Int,
thumb: Option[PhotoSize] = None,
file_size: Option[Int] = None)
case class ChatPhoto(small_file_id: String, big_file_id: String)
case class Chat(id: Long,
`type`: String,
title: Option[String] = None,
username: Option[String] = None,
first_name: Option[String] = None,
last_name: Option[String] = None,
all_members_are_administrators: Option[Boolean] = None,
photo: Option[ChatPhoto] = None,
description: Option[String] = None,
invite_link: Option[String] = None,
pinned_message: Option[Message] = None,
sticker_set_name: Option[String] = None,
can_set_sticker_set: Option[Boolean] = None)
case class Game(title: String,
description: String,
photo: Seq[PhotoSize],
text: Option[String] = None,
text_entities: Option[Seq[MessageEntity]] = None,
animation: Option[Animation] = None)
case class Animation(file_id: String,
thumb: Option[PhotoSize] = None,
file_name: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None)
case class InputFile()
case class Venue(location: Location,
title: String,
address: String,
foursquare_id: Option[String] = None)
case class Invoice(title: String,
description: String,
start_parameter: String,
currency: String,
total_amount: Int)
case class SuccessfulPayment(currency: String,
total_amount: Int,
invoice_payload: String,
shipping_option_id: Option[String] = None,
order_info: Option[OrderInfo] = None,
telegram_payment_charge_id: String,
provider_payment_charge_id: String)
case class Webhook(url: String,
certificate: Option[InputFile] = None,
max_connections: Option[Int] = None,
allowed_updates: Option[Seq[String]] = None)
case class WebhookInfo(url: String,
has_custom_certificate: Boolean,
pending_update_count: Int,
last_error_date: Option[Int] = None,
last_error_message: Option[String] = None,
max_connections: Option[Int] = None,
allowed_updates: Option[Seq[String]] = None)
}

View File

@@ -1,31 +0,0 @@
package eu.xeppaka.bot1
import akka.actor.ActorSystem
import com.vk.api.sdk.client.VkApiClient
import com.vk.api.sdk.client.actors.UserActor
import com.vk.api.sdk.httpclient.HttpTransportClient
object VkApiTests {
private val APP_ID = 6472591
private val APP_SECRET = "iGClqTncCZpzEqD8m5Wt"
private val USER_SECRET_CODE = "4180052ce419f0470d"
private val vkTransportClient = HttpTransportClient.getInstance()
private val vkApiClient = new VkApiClient(vkTransportClient)
// private val actorSystem = ActorSystem("vk")
def main(args: Array[String]): Unit = {
// val accessTokenActor = actorSystem.actorOf(AccessTokenActor.props)
// accessTokenActor ! GetToken(APP_ID)
// val authResponse = vkApiClient.oauth()
// .userAuthorizationCodeFlow(APP_ID, APP_SECRET, "https://oauth.vk.com/blank.html", USER_SECRET_CODE)
// .execute()
val userActor = new UserActor(6242549, "e7acb4be42aa11ab692b567ae272f16ec3c49d20f51851bb507126ce9ffc0f4aea013d34c0a26e285202d")
val resp = vkApiClient.messages().get(userActor).execute()
println(resp)
}
}

View File

@@ -1,40 +0,0 @@
package eu.xeppaka.bot1.actors
import akka.actor.{Actor, Props}
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpEntity, HttpRequest, HttpResponse, StatusCodes}
import akka.stream.{ActorMaterializer, ActorMaterializerSettings}
import eu.xeppaka.bot1.actors.AccessTokenActor.GetToken
import scala.concurrent.duration._
class AccessTokenActor extends Actor {
import akka.pattern.pipe
import context.dispatcher
private val OAUTH_URL_TEMPLATE = "https://oauth.vk.com/authorize?client_id=%d&scope=friends&redirect_uri=https://oauth.vk.com/blank.html&display=page&v=5.74&response_type=token"
private implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system))
private val http = Http(context.system)
override def receive: Receive = {
case GetToken(clientId) =>
val uri = OAUTH_URL_TEMPLATE.format(clientId)
val request = HttpRequest(uri = uri)
http.singleRequest(request).pipeTo(self)
case HttpResponse(StatusCodes.OK, headers, entity, _) =>
println("OK")
entity.withoutSizeLimit().toStrict(1 second).pipeTo(self)
case resp @ HttpResponse(code, _, _, _) =>
println(s"Failed with HTTP code $code")
case HttpEntity.Strict(contentType, data) =>
println(data.utf8String)
}
}
object AccessTokenActor {
case class GetToken(clientId: Int)
def props: Props = Props[AccessTokenActor]
}

View File

@@ -1,49 +0,0 @@
package eu.xeppaka.bot1.actors
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}
object ChatActor {
sealed trait ChatCommand {
def replyTo: ActorRef[Response]
}
case class Response(text: String)
case class Help(replyTo: ActorRef[Response]) extends ChatCommand
case class Start(replyTo: ActorRef[Response]) extends ChatCommand
case class MessageReceived(text: String, replyTo: ActorRef[Response]) extends ChatCommand
case class Stop(replyTo: ActorRef[Response]) extends ChatCommand
private val started: Behavior[ChatCommand] = Behaviors.receive { (ctx, msg) =>
msg match {
case MessageReceived(text, replyTo) =>
replyTo ! Response(s"Ok, you said: $text")
Behaviors.same
case Help(replyTo) =>
replyHelp(replyTo)
Behaviors.same
case Stop(replyTo) =>
replyTo ! Response("Bye, bye!")
initial
case _ =>
Behaviors.unhandled
}
}
private val initial: Behavior[ChatCommand] = Behaviors.receive { (ctx, msg) =>
msg match {
case Start(replyTo) =>
replyTo ! Response("You started. Try /help, motherfucker...")
started
case c: ChatCommand =>
c.replyTo ! Response("Only /start command is supported. Try it...")
Behaviors.same
}
}
private def replyHelp(replyTo: ActorRef[Response]): Unit = {
replyTo ! Response("No help is provided for such motherfuckers like you! But... ok, send /stop and we are free.")
}
val behavior: Behavior[ChatCommand] = initial
}

View File

@@ -1,78 +0,0 @@
package eu.xeppaka.bot1.actors
import akka.NotUsed
import akka.actor.typed.receptionist.Receptionist.Find
import akka.actor.typed.receptionist.{Receptionist, ServiceKey}
import akka.actor.typed.scaladsl.{ActorContext, Behaviors}
import akka.actor.typed.{ActorRef, Behavior}
import eu.xeppaka.bot1.TelegramEntities
import eu.xeppaka.bot1.actors.ChatActor.{Help, MessageReceived, Start, Stop}
object UpdateActor {
object BotMessages {
val start = "/start"
val stop = "/stop"
val help = "/help"
}
sealed trait UpdateCommand
case class UpdateReceived(update: TelegramEntities.Update, replyTo: ActorRef[UpdateResponse]) extends UpdateCommand
case class UpdateResponse(chatId: Long, text: String)
val behavior: Behavior[UpdateCommand] = Behaviors.receive[UpdateCommand] { (ctx, msg) =>
msg match {
case UpdateReceived(receivedUpdate, replyTo) =>
if (receivedUpdate.message.isDefined) {
ctx.spawn(processMessage(ctx, receivedUpdate.message.get, replyTo), s"process-update-${receivedUpdate.update_id}")
}
Behaviors.same
}
}
private def processMessage(parentContext: ActorContext[UpdateCommand], message: TelegramEntities.Message, replyTo: ActorRef[UpdateResponse]): Behavior[NotUsed] = {
Behaviors.setup[AnyRef] { ctx =>
val chatId = message.chat.id
val chatKey = ServiceKey[ChatActor.ChatCommand](chatId.toString)
println(s"Sending Find to receptionist to find actor with id: $chatId")
ctx.system.receptionist ! Find(chatKey, ctx.self.narrow[Receptionist.Listing])
Behaviors.receive[AnyRef] { (ctx, msg) =>
msg match {
case chatKey.Listing(listing) =>
if (listing.isEmpty) {
println(s"Actor with id: $chatId not found")
} else {
println(s"Actor with id: $chatId is found")
}
val chat = listing.headOption
.getOrElse({
val chatActor = parentContext.spawn(ChatActor.behavior, chatId.toString)
ctx.system.receptionist ! Receptionist.Register(chatKey, chatActor)
chatActor
})
chat ! getChatMessage(message, ctx.self.narrow[ChatActor.Response])
Behaviors.same
case ChatActor.Response(text) =>
replyTo ! UpdateResponse(chatId, text)
Behaviors.stopped
}
}
}
}.narrow[NotUsed]
private def getChatMessage(message: TelegramEntities.Message, replyTo: ActorRef[ChatActor.Response]): ChatActor.ChatCommand = {
import BotMessages._
message.text.getOrElse(help) match {
case `start` => Start(replyTo)
case `stop` => Stop(replyTo)
case `help` => Help(replyTo)
case msgText@_ => MessageReceived(msgText, replyTo)
}
}
}

View File

@@ -1,4 +1,4 @@
package eu.xeppaka.bot1
package eu.xeppaka.bot
import akka.http.scaladsl.model.Uri
@@ -6,14 +6,9 @@ case class BotUri(botId: String) {
private val baseUri = Uri(s"https://api.telegram.org/bot$botId")
val botUri: Uri = baseUri
val getMe: Uri = baseUri.withPath(baseUri.path / "getMe")
val setWebhook: Uri = baseUri.withPath(baseUri.path / "setWebhook")
val deleteWebhook: Uri = baseUri.withPath(baseUri.path / "deleteWebhook")
val getWebhookInfo: Uri = baseUri.withPath(baseUri.path / "getWebhookInfo")
val sendMessage: Uri = baseUri.withPath(baseUri.path / "sendMessage")
}

View File

@@ -0,0 +1,30 @@
package eu.xeppaka.bot
import akka.actor.Scheduler
import akka.actor.typed.scaladsl.AskPattern._
import akka.actor.typed.{ActorSystem, DispatcherSelector}
import eu.xeppaka.bot.TelegramBot._
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContextExecutor, Future}
import scala.io.StdIn
object Main {
def main(args: Array[String]): Unit = {
val botId = "570855144:AAEv7b817cuq2JJI9f2kG5B9G3zW1x-btz4"
val telegramBot = ActorSystem(TelegramBot.behavior(botId, "lenovo", 8443), "telegram-bot")
implicit val executionContext: ExecutionContextExecutor = telegramBot.dispatchers.lookup(DispatcherSelector.default())
implicit val scheduler: Scheduler = telegramBot.scheduler
val startFuture: Future[StartResult] = (telegramBot ? (ref => TelegramBot.Start(ref))) (10.seconds, scheduler)
println("Press enter to finish bot...")
StdIn.readLine()
val terminateFuture = startFuture
.flatMap { _ => (telegramBot ? (ref => TelegramBot.Stop(ref))) (10.seconds, scheduler) }
.andThen { case _ => telegramBot.terminate() }
Await.ready(terminateFuture, 5.seconds)
}
}

View File

@@ -0,0 +1,276 @@
package eu.xeppaka.bot
import java.io.InputStream
import java.security.{KeyStore, SecureRandom}
import java.util.UUID
import akka.Done
import akka.actor.ActorSystem
import akka.actor.typed.scaladsl.adapter._
import akka.actor.typed.scaladsl.{Behaviors, StashBuffer}
import akka.actor.typed.{ActorRef, Behavior, DispatcherSelector}
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives.{as, complete, entity, path, post}
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.{ConnectionContext, Http, HttpExt, HttpsConnectionContext}
import akka.stream.ActorMaterializer
import akka.util.ByteString
import eu.xeppaka.bot.TelegramEntities._
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory}
import scala.collection.immutable
import scala.concurrent.ExecutionContextExecutor
import scala.io.Source
import scala.util.{Failure, Success}
object TelegramBot {
sealed trait Command
sealed trait CommandResult
sealed trait StartResult extends CommandResult
sealed trait StopResult extends CommandResult
case object StartSuccess extends StartResult
case class StartFailure(exception: Throwable) extends StartResult
case object StopSuccess extends StopResult
case class StopFailure(exception: Throwable) extends StopResult
case class Start(replyTo: ActorRef[StartResult]) extends Command
case class Stop(replyTo: ActorRef[StopResult]) extends Command
case object GetBotInfo
case object GetWebhookInfo
def behavior(botId: String, interface: String, port: Int): Behavior[Command] = Behaviors.setup[Command] { ctx =>
implicit val untypedSystem: ActorSystem = ctx.system.toUntyped
implicit val actorMaterializer: ActorMaterializer = ActorMaterializer()
implicit val executionContextExecutor: ExecutionContextExecutor = ctx.system.dispatchers.lookup(DispatcherSelector.default())
val botUri = BotUri(botId)
val http: HttpExt = Http()
val hookId = UUID.randomUUID().toString
val webhookUri = Uri(s"https://xeppaka.eu:88/$hookId")
val httpsContext = createHttpsConnectionContext
val stashBuffer = StashBuffer[Command](10)
val updatesProcessor = ctx.spawn(UpdatesProcessor.behavior, "updatesProcessor")
val routes = botRoutes(hookId, updatesProcessor)
def reply(command: Command, exceptions: Seq[Throwable]): Unit = {
command match {
case Start(replyTo) =>
if (exceptions.isEmpty) {
ctx.log.info("action=start_bot result=success")
replyTo ! StartSuccess
} else {
ctx.log.error("action=start_bot result=failure", exceptions.head)
replyTo ! StartFailure(exceptions.head)
}
case Stop(replyTo) =>
if (exceptions.isEmpty) {
ctx.log.info("action=stop_bot result=success")
replyTo ! StopSuccess
} else {
ctx.log.error("action=stop_bot result=failure", exceptions.head)
replyTo ! StopFailure(exceptions.head)
}
case _ => throw new IllegalArgumentException(s"Unsupported command to reply: $command.")
}
}
def stopped(replyOnCommand: Option[Command] = None, exceptions: Seq[Throwable] = Seq.empty): Behavior[Command] = Behaviors.setup { ctx =>
replyOnCommand.foreach(reply(_, exceptions))
Behaviors.receiveMessage[Command] {
case startCommand@Start(_) =>
ctx.log.info("action=start_bot")
bindingServer(startCommand)
case stopCommand@Stop(_) =>
ctx.log.info("action=stop_bot")
reply(stopCommand, Seq.empty)
Behaviors.same
case _ =>
Behaviors.unhandled
}
}
def bindingServer(replyOnCommand: Command): Behavior[Command] = Behaviors.setup[Command] { ctx =>
case class BindingSuccess(binding: Http.ServerBinding) extends Command
case class BindingFailure(exception: Throwable) extends Command
ctx.log.info("action=bind_server interface={} port={}", interface, port)
http
.bindAndHandle(routes, interface, port, httpsContext)
.onComplete {
case Success(binding) => ctx.self ! BindingSuccess(binding)
case Failure(exception) => ctx.self ! BindingFailure(exception)
}
Behaviors.receiveMessage[Command] {
case BindingSuccess(binding) =>
ctx.log.info("action=bind_server result=success")
stashBuffer.unstashAll(ctx, settingWebhook(binding, replyOnCommand, Seq.empty))
case BindingFailure(exception) =>
ctx.log.error("action=bind_server result=failure", exception)
stashBuffer.unstashAll(ctx, stopped(Some(replyOnCommand), Seq(exception)))
case otherCommand: Command =>
stashBuffer.stash(otherCommand)
Behaviors.same
}
}
def unbindingServer(binding: Http.ServerBinding, replyOnCommand: Command, exceptions: Seq[Throwable]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
case object UnbindingSuccess extends Command
case class UnbindingFailure(exception: Throwable) extends Command
ctx.log.info("action=unbind_server interface={} port={}", interface, port)
binding
.unbind()
.onComplete {
case Success(Done) => ctx.self ! UnbindingSuccess
case Failure(exception) => ctx.self ! UnbindingFailure(exception)
}
Behaviors.receiveMessage[Command] {
case UnbindingSuccess =>
ctx.log.info("action=unbind_server result=success")
stashBuffer.unstashAll(ctx, stopped(Some(replyOnCommand), exceptions))
case UnbindingFailure(exception) =>
ctx.log.error("action=unbind_server result=failure", exception)
stashBuffer.unstashAll(ctx, stopped(Some(replyOnCommand), exceptions :+ exception))
case otherCommand: Command =>
stashBuffer.stash(otherCommand)
Behaviors.same
}
}
def settingWebhook(binding: Http.ServerBinding, replyOnCommand: Command, exceptions: Seq[Throwable]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
case object SetWebhookSuccess extends Command
case class SetWebhookFailure(exception: Throwable) extends Command
ctx.log.info("action=set_webhook url={} webhook={}", botUri.setWebhook, webhookUri)
implicit val executionContextExecutor: ExecutionContextExecutor = ctx.system.dispatchers.lookup(DispatcherSelector.default())
val urlEntity = HttpEntity.Strict(ContentTypes.`text/plain(UTF-8)`, ByteString(webhookUri.toString()))
val urlPart = Multipart.FormData.BodyPart.Strict("url", urlEntity)
val certificate = ByteString(Source.fromResource("telegram-bot.pem").mkString)
val certificateEntity = HttpEntity.Strict(ContentTypes.`application/octet-stream`, certificate)
val certificatePart = Multipart.FormData.BodyPart.Strict("certificate", certificateEntity, Map("filename" -> "telegram-bot.pem"))
val setWebhookFormData = Multipart.FormData.Strict(immutable.Seq(urlPart, certificatePart))
Marshal(setWebhookFormData)
.to[RequestEntity]
.flatMap(requestEntity => http.singleRequest(HttpRequest(uri = botUri.setWebhook, method = HttpMethods.POST, entity = requestEntity)))
.onComplete {
case Success(response) =>
if (response.status.isSuccess())
ctx.self ! SetWebhookSuccess
else
ctx.self ! SetWebhookFailure(new RuntimeException(s"Set webhook HTTP response status: ${response.status.value}."))
case Failure(exception) =>
ctx.self ! SetWebhookFailure(exception)
}
Behaviors.receiveMessage {
case SetWebhookSuccess =>
ctx.log.info("action=set_webhook result=success")
stashBuffer.unstashAll(ctx, started(binding, replyOnCommand, exceptions))
case SetWebhookFailure(exception) =>
ctx.log.error("action=set_webhook result=failure", exception)
stashBuffer.unstashAll(ctx, unbindingServer(binding, replyOnCommand, exceptions :+ exception))
case otherCommand: Command =>
stashBuffer.stash(otherCommand)
Behaviors.same
}
}
def deletingWebhook(binding: Http.ServerBinding, replyOnCommand: Command, exceptions: Seq[Throwable]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
case object DeleteWebhookSuccess extends Command
case class DeleteWebhookFailure(exception: Throwable) extends Command
ctx.log.info("action=delete_webhook url={} webhook={}", botUri.deleteWebhook, webhookUri)
implicit val executionContextExecutor: ExecutionContextExecutor = ctx.system.dispatchers.lookup(DispatcherSelector.default())
http
.singleRequest(HttpRequest(uri = botUri.deleteWebhook, method = HttpMethods.POST))
.onComplete {
case Success(response) =>
if (response.status.isSuccess())
ctx.self ! DeleteWebhookSuccess
else
ctx.self ! DeleteWebhookFailure(new RuntimeException(s"Delete webhook HTTP response status: ${response.status.value}"))
case Failure(exception) =>
ctx.self ! DeleteWebhookFailure(exception)
}
Behaviors.receiveMessage {
case DeleteWebhookSuccess =>
ctx.log.info("action=delete_webhook result=success")
stashBuffer.unstashAll(ctx, unbindingServer(binding, replyOnCommand, exceptions))
case DeleteWebhookFailure(exception) =>
ctx.log.error("action=delete_webhook result=failure", exception)
stashBuffer.unstashAll(ctx, unbindingServer(binding, replyOnCommand, exceptions :+ exception))
case otherCommand: Command =>
stashBuffer.stash(otherCommand)
Behaviors.same
}
}
def started(binding: Http.ServerBinding, replyOnCommand: Command, exceptions: Seq[Throwable]): Behavior[Command] = Behaviors.setup[Command] { ctx =>
reply(replyOnCommand, exceptions)
Behaviors.receiveMessage[Command] {
case startCommand@Start(_) =>
ctx.log.info("action=start_bot")
reply(startCommand, Seq.empty)
Behaviors.same
case stopCommand@Stop(_) =>
ctx.log.info("action=stop_bot")
deletingWebhook(binding, stopCommand, exceptions)
case _ =>
Behaviors.unhandled
}
}
stopped()
}
private def botRoutes(hookId: String, updatesProcessor: ActorRef[UpdatesProcessor.Command]): Route = {
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport._
import io.circe.generic.auto._
path(hookId) {
post {
entity(as[Update]) { update =>
updatesProcessor ! UpdatesProcessor.UpdateReceived(update)
complete(HttpResponse())
}
}
}
}
private def createHttpsConnectionContext: HttpsConnectionContext = {
val password: Array[Char] = "".toCharArray // do not store passwords in code, read them from somewhere safe!
val ks: KeyStore = KeyStore.getInstance("PKCS12")
val keystore: InputStream = getClass.getResourceAsStream("/telegram-bot.p12")
require(keystore != null, "Keystore required!")
ks.load(keystore, password)
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509")
keyManagerFactory.init(ks, password)
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509")
tmf.init(ks)
val sslContext: SSLContext = SSLContext.getInstance("TLS")
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
ConnectionContext.https(sslContext)
}
}

View File

@@ -0,0 +1,278 @@
package eu.xeppaka.bot
object TelegramEntities {
case class Response[T](ok: Boolean,
description: Option[String] = None,
error_code: Option[Int] = None,
result: T
)
case class GetMe(id: Int, is_bot: Boolean, first_name: String, username: String)
case class InlineQuery(id: String,
from: User,
location: Location,
query: String,
offset: String
)
case class Location(longitude: Float,
latitude: Float
)
case class Update(update_id: Int,
message: Option[Message] = None,
edited_message: Option[Message] = None,
channel_post: Option[Message] = None,
edited_channel_post: Option[Message] = None,
inline_query: Option[InlineQuery] = None,
chosen_inline_result: Option[ChosenInlineResult] = None,
callback_query: Option[CallbackQuery] = None,
shipping_query: Option[ShippingQuery] = None,
pre_checkout_query: Option[PreCheckoutQuery] = None
)
case class ChosenInlineResult(result_id: String,
from: User,
location: Option[Location] = None,
inline_message_id: Option[String] = None,
query: String
)
case class CallbackQuery(id: String,
from: User,
message: Option[Message] = None,
inline_message_id: Option[String] = None,
chat_instance: String,
data: Option[String] = None,
game_short_name: Option[String] = None
)
case class ShippingQuery(id: String,
from: User,
invoice_payload: String,
shipping_address: ShippingAddress
)
case class ShippingAddress(country_code: String,
state: String,
city: String,
street_line1: String,
street_line2: String,
post_code: String
)
case class PreCheckoutQuery(id: String,
from: User,
currency: String,
total_amount: Int,
invoice_payload: String,
shipping_option_id: Option[String] = None,
order_info: Option[OrderInfo] = None
)
case class OrderInfo(name: Option[String] = None,
phone_number: Option[String] = None,
email: Option[String] = None,
shipping_address: Option[ShippingAddress] = None
)
case class User(id: Int,
is_bot: Boolean,
first_name: String,
last_name: Option[String] = None,
username: Option[String] = None,
language_code: Option[String] = None
)
case class SendMessage(chat_id: Long,
text: String,
parse_mode: Option[String] = None,
disable_web_page_preview: Option[Boolean] = None,
disable_notification: Option[Boolean] = None,
reply_to_message_id: Option[Int] = None,
reply_markup: Option[String] = None
)
case class Message(message_id: Int,
from: Option[User] = None,
date: Int,
chat: Chat,
forward_from: Option[User] = None,
forward_from_chat: Option[User] = None,
forward_from_message_id: Option[Int] = None,
forward_signature: Option[String] = None,
forward_date: Option[Int] = None,
reply_to_message: Option[Message] = None,
edit_date: Option[Int] = None,
media_group_id: Option[String] = None,
author_signature: Option[String] = None,
text: Option[String] = None,
entities: Option[Seq[MessageEntity]] = None,
caption_entities: Option[Seq[MessageEntity]] = None,
audio: Option[Audio] = None,
document: Option[Document] = None,
game: Option[Game] = None,
photo: Option[Seq[PhotoSize]] = None,
sticker: Option[Sticker] = None,
video: Option[Video] = None,
voice: Option[Voice] = None,
video_note: Option[VideoNote] = None,
caption: Option[String] = None,
contact: Option[Contact] = None,
location: Option[Location] = None,
venue: Option[Venue] = None,
new_chat_members: Option[Seq[User]] = None,
left_chat_member: Option[Seq[User]] = None,
new_chat_title: Option[String] = None,
new_chat_photo: Option[Seq[PhotoSize]] = None,
delete_chat_photo: Option[Boolean] = None,
group_chat_created: Option[Boolean] = None,
supergroup_chat_created: Option[Boolean] = None,
channel_chat_created: Option[Boolean] = None,
migrate_to_chat_id: Option[Int] = None,
migrate_from_chat_id: Option[Int] = None,
pinned_message: Option[Message] = None,
invoice: Option[Invoice] = None,
successful_payment: Option[SuccessfulPayment] = None,
connected_website: Option[String] = None
)
case class MessageEntity(`type`: String,
offset: Int,
length: Int,
url: Option[String] = None,
user: Option[User] = None
)
case class Contact(phone_number: String,
first_name: String,
last_name: Option[String] = None,
user_id: Option[Int] = None
)
case class Sticker(file_id: String,
width: Int,
height: Int,
thumb: Option[PhotoSize] = None,
emoji: Option[String] = None,
set_name: Option[String] = None,
mask_position: Option[String] = None,
file_size: Option[Int] = None
)
case class Video(file_id: String,
width: Int,
height: Int,
duration: Int,
thumb: Option[PhotoSize] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None
)
case class Audio(file_id: String,
duration: Int,
performer: Option[String] = None,
title: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None
)
case class Document(file_id: String,
thumb: Option[PhotoSize] = None,
file_name: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None
)
case class PhotoSize(file_id: String,
width: Int,
height: Int,
file_size: Option[Int] = None
)
case class Voice(file_id: String,
duration: Int,
mime_type: Option[String] = None,
file_size: Option[Int] = None
)
case class VideoNote(file_id: String,
length: Int,
duration: Int,
thumb: Option[PhotoSize] = None,
file_size: Option[Int] = None
)
case class ChatPhoto(small_file_id: String, big_file_id: String)
case class Chat(id: Long,
`type`: String,
title: Option[String] = None,
username: Option[String] = None,
first_name: Option[String] = None,
last_name: Option[String] = None,
all_members_are_administrators: Option[Boolean] = None,
photo: Option[ChatPhoto] = None,
description: Option[String] = None,
invite_link: Option[String] = None,
pinned_message: Option[Message] = None,
sticker_set_name: Option[String] = None,
can_set_sticker_set: Option[Boolean] = None
)
case class Game(title: String,
description: String,
photo: Seq[PhotoSize],
text: Option[String] = None,
text_entities: Option[Seq[MessageEntity]] = None,
animation: Option[Animation] = None
)
case class Animation(file_id: String,
thumb: Option[PhotoSize] = None,
file_name: Option[String] = None,
mime_type: Option[String] = None,
file_size: Option[Int] = None
)
case class InputFile()
case class Venue(location: Location,
title: String,
address: String,
foursquare_id: Option[String] = None
)
case class Invoice(title: String,
description: String,
start_parameter: String,
currency: String,
total_amount: Int
)
case class SuccessfulPayment(currency: String,
total_amount: Int,
invoice_payload: String,
shipping_option_id: Option[String] = None,
order_info: Option[OrderInfo] = None,
telegram_payment_charge_id: String,
provider_payment_charge_id: String
)
case class Webhook(url: String,
certificate: Option[InputFile] = None,
max_connections: Option[Int] = None,
allowed_updates: Option[Seq[String]] = None
)
case class WebhookInfo(url: String,
has_custom_certificate: Boolean,
pending_update_count: Int,
last_error_date: Option[Int] = None,
last_error_message: Option[String] = None,
max_connections: Option[Int] = None,
allowed_updates: Option[Seq[String]] = None
)
}

View File

@@ -0,0 +1,20 @@
package eu.xeppaka.bot
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import eu.xeppaka.bot.TelegramEntities.Update
object UpdatesProcessor {
sealed trait Command
sealed trait CommandResult
case class UpdateReceived(update: Update) extends Command
def behavior: Behavior[Command] = Behaviors.receive[Command] { (ctx, msg) =>
msg match {
case UpdateReceived(update) =>
ctx.log.info("action=update_received update={}", update)
Behaviors.same
}
}
}