commit 0358ec5e3b6480d397e3a7e71454157c2f25a59f Author: Rubenlagus Date: Wed Jun 24 23:45:34 2015 +0200 Initial code diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..23b437c Binary files /dev/null and b/.DS_Store differ diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..a26198f --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +BotAPi \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..434b83a --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,32 @@ + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..49c237d --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..2b44d22 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,39 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/jsLinters/jshint.xml b/.idea/jsLinters/jshint.xml new file mode 100644 index 0000000..11b997b --- /dev/null +++ b/.idea/jsLinters/jshint.xml @@ -0,0 +1,71 @@ + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_5_1.xml new file mode 100644 index 0000000..d0c6958 --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_5_1.xml new file mode 100644 index 0000000..dd95d35 --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_5_1.xml new file mode 100644 index 0000000..83e4ab9 --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_base_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_base_2_5_1.xml new file mode 100644 index 0000000..0488e16 --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_base_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_json_provider_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_json_provider_2_5_1.xml new file mode 100644 index 0000000..7898c3d --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_jaxrs_jackson_jaxrs_json_provider_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_jaxb_annotations_2_5_1.xml b/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_jaxb_annotations_2_5_1.xml new file mode 100644 index 0000000..618a1f5 --- /dev/null +++ b/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_jaxb_annotations_2_5_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_bundle_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_bundle_1_19.xml new file mode 100644 index 0000000..015dbda --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_bundle_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_core_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_core_1_19.xml new file mode 100644 index 0000000..3ac29b2 --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_core_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_1_19.xml new file mode 100644 index 0000000..8734ae4 --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_servlet_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_servlet_1_19.xml new file mode 100644 index 0000000..2f3fb72 --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_grizzly2_servlet_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_server_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_server_1_19.xml new file mode 100644 index 0000000..47f74e7 --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_server_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__com_sun_jersey_jersey_servlet_1_19.xml b/.idea/libraries/Maven__com_sun_jersey_jersey_servlet_1_19.xml new file mode 100644 index 0000000..882f842 --- /dev/null +++ b/.idea/libraries/Maven__com_sun_jersey_jersey_servlet_1_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_codec_commons_codec_1_9.xml b/.idea/libraries/Maven__commons_codec_commons_codec_1_9.xml new file mode 100644 index 0000000..a66d039 --- /dev/null +++ b/.idea/libraries/Maven__commons_codec_commons_codec_1_9.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_io_commons_io_2_4.xml b/.idea/libraries/Maven__commons_io_commons_io_2_4.xml new file mode 100644 index 0000000..bc2aad0 --- /dev/null +++ b/.idea/libraries/Maven__commons_io_commons_io_2_4.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml b/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml new file mode 100644 index 0000000..eab40b3 --- /dev/null +++ b/.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_2.xml b/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_2.xml new file mode 100644 index 0000000..e35b936 --- /dev/null +++ b/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_0_1.xml b/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_0_1.xml new file mode 100644 index 0000000..63d10f6 --- /dev/null +++ b/.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_validation_validation_api_1_1_0_Final.xml b/.idea/libraries/Maven__javax_validation_validation_api_1_1_0_Final.xml new file mode 100644 index 0000000..940ce73 --- /dev/null +++ b/.idea/libraries/Maven__javax_validation_validation_api_1_1_0_Final.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_ws_rs_javax_ws_rs_api_2_0_1.xml b/.idea/libraries/Maven__javax_ws_rs_javax_ws_rs_api_2_0_1.xml new file mode 100644 index 0000000..c890167 --- /dev/null +++ b/.idea/libraries/Maven__javax_ws_rs_javax_ws_rs_api_2_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_ws_rs_jsr311_api_1_1_1.xml b/.idea/libraries/Maven__javax_ws_rs_jsr311_api_1_1_1.xml new file mode 100644 index 0000000..a0c4d76 --- /dev/null +++ b/.idea/libraries/Maven__javax_ws_rs_jsr311_api_1_1_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_35.xml b/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_35.xml new file mode 100644 index 0000000..da90023 --- /dev/null +++ b/.idea/libraries/Maven__mysql_mysql_connector_java_5_1_35.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_5.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_5.xml new file mode 100644 index 0000000..6d383f2 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_4_1.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_4_1.xml new file mode 100644 index 0000000..24a6857 --- /dev/null +++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_4_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_httpcomponents_httpmime_4_5.xml b/.idea/libraries/Maven__org_apache_httpcomponents_httpmime_4_5.xml new file mode 100644 index 0000000..b47fa2b --- /dev/null +++ b/.idea/libraries/Maven__org_apache_httpcomponents_httpmime_4_5.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_framework_2_3_19.xml b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_framework_2_3_19.xml new file mode 100644 index 0000000..0e72c5a --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_framework_2_3_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_2_3_19.xml b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_2_3_19.xml new file mode 100644 index 0000000..3d9c159 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_2_3_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_server_2_3_19.xml b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_server_2_3_19.xml new file mode 100644 index 0000000..b807cb3 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_server_2_3_19.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_servlet_2_2_16.xml b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_servlet_2_2_16.xml new file mode 100644 index 0000000..65fc7b2 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_grizzly_grizzly_http_servlet_2_2_16.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_external_aopalliance_repackaged_2_4_0_b12.xml b/.idea/libraries/Maven__org_glassfish_hk2_external_aopalliance_repackaged_2_4_0_b12.xml new file mode 100644 index 0000000..9900ea1 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_external_aopalliance_repackaged_2_4_0_b12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_external_javax_inject_2_4_0_b12.xml b/.idea/libraries/Maven__org_glassfish_hk2_external_javax_inject_2_4_0_b12.xml new file mode 100644 index 0000000..22eb972 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_external_javax_inject_2_4_0_b12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_hk2_api_2_4_0_b12.xml b/.idea/libraries/Maven__org_glassfish_hk2_hk2_api_2_4_0_b12.xml new file mode 100644 index 0000000..dd75e09 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_hk2_api_2_4_0_b12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_hk2_locator_2_4_0_b12.xml b/.idea/libraries/Maven__org_glassfish_hk2_hk2_locator_2_4_0_b12.xml new file mode 100644 index 0000000..3fd5a97 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_hk2_locator_2_4_0_b12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_hk2_utils_2_4_0_b12.xml b/.idea/libraries/Maven__org_glassfish_hk2_hk2_utils_2_4_0_b12.xml new file mode 100644 index 0000000..2bc8886 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_hk2_utils_2_4_0_b12.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_hk2_osgi_resource_locator_1_0_1.xml b/.idea/libraries/Maven__org_glassfish_hk2_osgi_resource_locator_1_0_1.xml new file mode 100644 index 0000000..3d299fa --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_hk2_osgi_resource_locator_1_0_1.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_bundles_repackaged_jersey_guava_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_bundles_repackaged_jersey_guava_2_18.xml new file mode 100644 index 0000000..99b92a1 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_bundles_repackaged_jersey_guava_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_containers_jersey_container_grizzly2_http_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_containers_jersey_container_grizzly2_http_2_18.xml new file mode 100644 index 0000000..2318d58 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_containers_jersey_container_grizzly2_http_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_client_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_client_2_18.xml new file mode 100644 index 0000000..b7d8fb1 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_client_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_common_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_common_2_18.xml new file mode 100644 index 0000000..57e4677 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_common_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_server_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_server_2_18.xml new file mode 100644 index 0000000..ab8278a --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_core_jersey_server_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_ext_jersey_entity_filtering_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_ext_jersey_entity_filtering_2_18.xml new file mode 100644 index 0000000..71f8355 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_ext_jersey_entity_filtering_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_jaxb_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_jaxb_2_18.xml new file mode 100644 index 0000000..0ab63a6 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_jaxb_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_json_jackson_2_18.xml b/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_json_jackson_2_18.xml new file mode 100644 index 0000000..d116180 --- /dev/null +++ b/.idea/libraries/Maven__org_glassfish_jersey_media_jersey_media_json_jackson_2_18.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_javassist_javassist_3_18_1_GA.xml b/.idea/libraries/Maven__org_javassist_javassist_3_18_1_GA.xml new file mode 100644 index 0000000..1f90730 --- /dev/null +++ b/.idea/libraries/Maven__org_javassist_javassist_3_18_1_GA.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_json_json_20141113.xml b/.idea/libraries/Maven__org_json_json_20141113.xml new file mode 100644 index 0000000..2f486e7 --- /dev/null +++ b/.idea/libraries/Maven__org_json_json_20141113.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/Maven__org_jsoup_jsoup_1_8_2.xml b/.idea/libraries/Maven__org_jsoup_jsoup_1_8_2.xml new file mode 100644 index 0000000..b78f11b --- /dev/null +++ b/.idea/libraries/Maven__org_jsoup_jsoup_1_8_2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..3bb9901 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /usr/local/bin/bower + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d845503 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..d190f54 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,1671 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Java + + + Probable bugsJava + + + + + MalformedFormatString + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1434808152627 + + + 1434854005227 + + + 1434896932077 + + + 1434896979344 + + + 1434904623863 + + + 1434905289461 + + + 1434921270136 + + + 1434922215775 + + + 1434926059465 + + + 1434927087970 + + + 1434928139587 + + + 1434932225092 + + + 1434956620075 + + + 1434956750907 + + + 1434999490285 + + + 1435015027385 + + + 1435084167629 + + + 1435108063150 + + + 1435169633681 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/src/main/java/org/telegram/api/PhotoSize.java + 32 + + + + file://$PROJECT_DIR$/src/main/java/org/telegram/services/WeatherService.java + 124 + + + + file://$PROJECT_DIR$/src/main/java/org/telegram/services/DirectionsService.java + 69 + + + + file://$PROJECT_DIR$/src/main/java/org/telegram/services/DirectionsService.java + 78 + + + + jar://$MAVEN_REPOSITORY$/org/glassfish/grizzly/grizzly-framework/2.3.19/grizzly-framework-2.3.19-sources.jar!/org/glassfish/grizzly/ssl/SSLContextConfigurator.java + 359 + + + + file://$PROJECT_DIR$/src/main/java/org/telegram/updatesreceivers/Webhook.java + 60 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No facets are configured + + + + + + + + ExtJS + + + + + + + + 1.8 + + + + + + + + BotAPi + + + + + + + + 1.8 + + + + + + + + Maven: org.json:json:20141113 + + + + + + + + \ No newline at end of file diff --git a/BotAPi.iml b/BotAPi.iml new file mode 100644 index 0000000..11ee1a7 --- /dev/null +++ b/BotAPi.iml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7d05241 --- /dev/null +++ b/pom.xml @@ -0,0 +1,165 @@ + + + 4.0.0 + + BotApi + org.telegram.botapi + 1.0 + + + UTF-8 + UTF-8 + 2.18 + + + + + + org.glassfish.jersey + jersey-bom + ${jersey.version} + pom + import + + + + + + + org.glassfish.jersey.containers + jersey-container-grizzly2-http + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} + + + com.sun.jersey + jersey-bundle + 1.19 + + + com.sun.jersey + jersey-grizzly2-servlet + 1.19 + + + org.json + json + 20141113 + + + org.apache.httpcomponents + httpclient + 4.4.1 + + + commons-io + commons-io + 2.4 + + + org.apache.httpcomponents + httpclient + 4.5 + + + org.apache.httpcomponents + httpmime + 4.5 + + + mysql + mysql-connector-java + 5.1.35 + + + org.jsoup + jsoup + 1.8.2 + + + + + ${project.basedir}/target + ${project.build.directory}/classes + ${project.artifactId}-${project.version} + ${project.build.directory}/test-classes + ${project.basedir}/src/main/java + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + org.telegram.Main + + + + + + maven-assembly-plugin + + + + org.telegram.Main + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.9 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/lib + false + false + true + + + + + + maven-clean-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + + + \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..21ed6df Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 0000000..9d2a42b Binary files /dev/null and b/src/main/.DS_Store differ diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store new file mode 100644 index 0000000..d6fa824 Binary files /dev/null and b/src/main/java/.DS_Store differ diff --git a/src/main/java/org/telegram/BotConfig.java b/src/main/java/org/telegram/BotConfig.java new file mode 100644 index 0000000..1bf8c3a --- /dev/null +++ b/src/main/java/org/telegram/BotConfig.java @@ -0,0 +1,14 @@ +package org.telegram; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Bots configurations + * @date 20 of June of 2015 + */ +public class BotConfig { + public static final String TOKENWEATHER = ""; + public static final String TOKENTRANSIFEX = ""; + public static final String TOKENFILES = ""; + public static final String TOKENDIRECTIONS = ""; +} diff --git a/src/main/java/org/telegram/BuildVars.java b/src/main/java/org/telegram/BuildVars.java new file mode 100644 index 0000000..fb0452c --- /dev/null +++ b/src/main/java/org/telegram/BuildVars.java @@ -0,0 +1,28 @@ +package org.telegram; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Custom build vars FILL EVERYTHING CORRECTLY + * @date 20 of June of 2015 + */ +public class BuildVars { + public static final Boolean debug = true; + public static final Boolean useWebHook = true; + public static final String BASEWEBHOOKURL = "http://YOUREXTERNALADDRES"; + public static final String INTERNALWEBHOOKURL = "http://localhost"; + + public static final String OPENWEATHERAPIKEY = ""; + + public static final String DirectionsApiKey = ""; + + public static final String TRANSIFEXUSER = ""; + public static final String TRANSIFEXPASSWORD = ""; + + public static final String pathToLogs = "./"; + + public static final String linkDB = "jdbc:mysql://localhost:3306/YOURDATABSENAME?useUnicode=true&characterEncoding=UTF-8"; + public static final String controllerDB = "com.mysql.jdbc.Driver"; + public static final String userDB = ""; + public static final String password = ""; +} diff --git a/src/main/java/org/telegram/Commands.java b/src/main/java/org/telegram/Commands.java new file mode 100644 index 0000000..7ac5bb6 --- /dev/null +++ b/src/main/java/org/telegram/Commands.java @@ -0,0 +1,43 @@ +package org.telegram; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Command for the bots + * @date 20 of June of 2015 + */ +public class Commands { + public static final String commandInitChar = "/"; + /// Weather forecast command + public static final String WEATHERCOMMAND = commandInitChar + "weather"; + /// Current wether command + public static final String CURRENTWEATHERCOMMAND = commandInitChar + "current"; + /// Transifex iOS command + public static final String transifexiOSCommand = commandInitChar + "langios"; + /// Transifex android command + public static final String transifexAndroidCommand = commandInitChar + "langdroid"; + /// Transifex android command + public static final String transifexTDesktop = commandInitChar + "langdesk"; + /// Transifex android command + public static final String transifexWebogram = commandInitChar + "langweb"; + /// Transifex android command + public static final String transifexWP = commandInitChar + "langwp"; + /// Transifex android command + public static final String transifexOSX = commandInitChar + "langosx"; + /// Transifex android support command + public static final String transifexAndroidSupportCommand = commandInitChar + "langtestdroid"; + /// Help command + public static final String help = commandInitChar + "help"; + /// Upload command + public static final String uploadCommand = commandInitChar + "upload"; + /// Start command + public static final String startCommand = commandInitChar + "start"; + /// Cancel command + public static final String cancelCommand = commandInitChar + "cancel"; + /// Delete command + public static final String deleteCommand = commandInitChar + "delete"; + /// List command + public static final String listCommand = commandInitChar + "list"; + /// Start directions command + public static final String startDirectionCommand = commandInitChar + "directions"; +} diff --git a/src/main/java/org/telegram/CustomMessages.java b/src/main/java/org/telegram/CustomMessages.java new file mode 100644 index 0000000..d5e85b4 --- /dev/null +++ b/src/main/java/org/telegram/CustomMessages.java @@ -0,0 +1,52 @@ +package org.telegram; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Custom messages to be sent to the user + * @date 21 of June of 2015 + */ +public class CustomMessages { + public static final String helpWeather = "Curious about the weather?\nJust send me these commands and you'll know a lot better.\n\n" + + "|-- " + Commands.WEATHERCOMMAND + " CITY,COUNTRY : Get a 3-day weather forecast for a city.\n" + + "|-- " + Commands.CURRENTWEATHERCOMMAND + " CITY,COUNTRY : Get the current weather of a city.\n" + + "|-- Send a location to get the forecast for it."; + public static final String helpTransifex = "Tricks with words is the game that I play, give it a shot, I might make your day.\n\n" + + "To get the latest Telegram localization file for a language: \n" + + "|-- " + Commands.transifexiOSCommand + " LANG_CODE : Get the latest iOS language.\n" + + "|-- " + Commands.transifexAndroidCommand + " LANG_CODE : Get the latest android language.\n" + + "|-- " + Commands.transifexWebogram + " LANG_CODE : Get the latest webogram language.\n" + + "|-- " + Commands.transifexiOSCommand + " LANG_CODE : Get the latest iOS language.\n" + + "|-- " + Commands.transifexAndroidCommand + " LANG_CODE : Get the latest android language.\n" + + "|-- " + Commands.transifexWebogram + " LANG_CODE : Get the latest webogram language.\n" + + "|-- " + Commands.transifexTDesktop + " LANG_CODE : Get the latest Tdesktop language.\n" + + "|-- " + Commands.transifexOSX + " LANG_CODE : Get the latest OSX-App language.\n" + + "|-- " + Commands.transifexWP + " LANG_CODE : Get the latest Windows Phone language.\n\n" + + "2. To get an updated localization file for your Android beta-app: \n" + + "|-- " + Commands.transifexAndroidSupportCommand + " LANG_CODE : Get the latest Android-beta language.\n\n"; + public static final String helpFiles = "Leaving a file for some others to find? Just dock your boat here and a bay comes to mind.\n\n"+ + "Share files through a custom link: \n" + + "|-- " + Commands.startCommand + " FILEID : Get a file by id.\n" + + "|-- " + Commands.uploadCommand + " : Start your file upload.\n" + + "|-- " + Commands.deleteCommand + " : Choose one of your files to delete it.\n" + + "|-- " + Commands.listCommand + " : Show a list of your shared files.\n\n"; + public static final String helpDirections = "The road ahead, paved with good intentions, the right path ahead however is what I tend to mention.\n\n" + + "To get directions between two locations: \n" + + "|-- " + Commands.startDirectionCommand + " : Start to get directions\n"; + + public static final String sendFileToUpload = "Please send me a file you want to share. Make sure you attach it as file, not as an image or video."; + public static final String fileUploaded = "Great, your file has been uploaded. Send this link to anyone you want and they will be able to download the file:\n\n"; + public static final String deleteUploadedFile = "Please select the file you want to delete:"; + public static final String fileDeleted = "The file was deleted"; + public static final String wrongFileId = "Sorry, we can't find a file with that ID. Either a typo was made or it was deleted already."; + public static final String listOfFiles = "This your currently shared files list:"; + public static final String noFiles = "You haven't shared any file yet."; + public static final String processFinished = "The current process was cancelled."; + public static final String uploadedFileURL = "https://telegram.me/filesbot?start="; + public static final String chooseFromRecentWeather = "Please choose an option from your recent requests:"; + + public static final String initDirections = "Please reply with your departing location."; + public static final String sendDestination = "Please reply with your destination."; + public static final String youNeedReplyDirections = "I'm sorry, I can't help you unless you reply to the message I sent you."; + public static final String pleaseSendMeCityWeather = "Send me the city and country you are interested in, use this format: CITY,COUNTRY"; +} diff --git a/src/main/java/org/telegram/Main.java b/src/main/java/org/telegram/Main.java new file mode 100644 index 0000000..9faf2bb --- /dev/null +++ b/src/main/java/org/telegram/Main.java @@ -0,0 +1,19 @@ +package org.telegram; + +import org.telegram.updateshandlers.*; +import org.telegram.updatesreceivers.UpdatesThread; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Main class to create all bots + * @date 20 of June of 2015 + */ +public class Main { + public static void main(String[] args) { + UpdatesCallback weatherBot = new WeatherHandlers(); + UpdatesCallback transifexBot = new TransifexHandlers(); + UpdatesCallback filesBot = new FilesHandlers(); + UpdatesCallback directionsBot = new DirectionsHandlers(); + } +} diff --git a/src/main/java/org/telegram/SenderHelper.java b/src/main/java/org/telegram/SenderHelper.java new file mode 100644 index 0000000..fa6c770 --- /dev/null +++ b/src/main/java/org/telegram/SenderHelper.java @@ -0,0 +1,137 @@ +package org.telegram; + +import org.apache.http.HttpEntity; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.json.JSONObject; +import org.telegram.api.Message; +import org.telegram.methods.Constants; +import org.telegram.methods.SendDocument; +import org.telegram.methods.SendMessage; +import org.telegram.methods.SetWebhook; + +import java.io.File; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Send Helper + * @date 20 of June of 2015 + */ +public class SenderHelper { + public static Message SendMessage(SendMessage message, String botToken) { + try { + CloseableHttpClient httpclient = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + String url = Constants.BASEURL + botToken + "/" + SendMessage.PATH; + HttpPost httppost = new HttpPost(url); + httppost.addHeader("Content-type", "application/x-www-form-urlencoded"); + httppost.addHeader("charset", "UTF-8"); + List nameValuePairs = new ArrayList(); + nameValuePairs.add(new BasicNameValuePair(SendMessage.CHATID_FIELD, message.getChatId().toString())); + nameValuePairs.add(new BasicNameValuePair(SendMessage.TEXT_FIELD, message.getText())); + if (message.getDisableWebPagePreview() != null) { + nameValuePairs.add(new BasicNameValuePair(SendMessage.DISABLEWEBPAGEPREVIEW_FIELD, message.getDisableWebPagePreview().toString())); + } + if (message.getReplayMarkup() != null) { + nameValuePairs.add(new BasicNameValuePair(SendMessage.REPLYMARKUP_FIELD, message.getReplayMarkup().toJson().toString())); + } + if (message.getReplayToMessageId() != null) { + nameValuePairs.add(new BasicNameValuePair(SendMessage.REPLYTOMESSAGEID_FIELD, message.getReplayToMessageId().toString())); + } + httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")); + CloseableHttpResponse response = httpclient.execute(httppost); + HttpEntity ht = response.getEntity(); + + BufferedHttpEntity buf = new BufferedHttpEntity(ht); + String responseContent = EntityUtils.toString(buf, "UTF-8"); + + JSONObject jsonObject = new JSONObject(responseContent); + if (!jsonObject.getBoolean("ok")) { + throw new InvalidObjectException(jsonObject.toString()); + } + JSONObject jsonMessage = jsonObject.getJSONObject("result"); + return new Message(jsonMessage); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public static void SendDocument(SendDocument sendDocument, String botToken) { + try { + CloseableHttpClient httpClient = HttpClients.createDefault(); + String url = Constants.BASEURL + botToken + "/" + SendDocument.PATH; + HttpPost httppost = new HttpPost(url); + + if (sendDocument.isNewDocument()) { + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.addTextBody(SendDocument.CHATID_FIELD, sendDocument.getChatId().toString()); + builder.addBinaryBody(SendDocument.DOCUMENT_FIELD, new File(sendDocument.getDocument()), ContentType.APPLICATION_OCTET_STREAM, sendDocument.getDocumentName()); + if (sendDocument.getReplayMarkup() != null) { + builder.addTextBody(SendDocument.REPLYMARKUP_FIELD, sendDocument.getReplayMarkup().toJson().toString()); + } + if (sendDocument.getReplayToMessageId() != null) { + builder.addTextBody(SendDocument.REPLYTOMESSAGEID_FIELD, sendDocument.getReplayToMessageId().toString()); + } + HttpEntity multipart = builder.build(); + httppost.setEntity(multipart); + } else { + List nameValuePairs = new ArrayList<>(); + nameValuePairs.add(new BasicNameValuePair(SendDocument.CHATID_FIELD, sendDocument.getChatId().toString())); + nameValuePairs.add(new BasicNameValuePair(SendDocument.DOCUMENT_FIELD, sendDocument.getDocument())); + if (sendDocument.getReplayMarkup() != null) { + nameValuePairs.add(new BasicNameValuePair(SendDocument.REPLYMARKUP_FIELD, sendDocument.getReplayMarkup().toString())); + } + if (sendDocument.getReplayToMessageId() != null) { + nameValuePairs.add(new BasicNameValuePair(SendDocument.REPLYTOMESSAGEID_FIELD, sendDocument.getReplayToMessageId().toString())); + } + httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")); + } + + CloseableHttpResponse response = httpClient.execute(httppost); + if (sendDocument.isNewDocument()) { + File fileToDelete = new File(sendDocument.getDocument()); + fileToDelete.delete(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void SendWebhook(String webHookURL, String botToken) { + try { + CloseableHttpClient httpclient = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + String url = Constants.BASEURL + botToken + "/" + SetWebhook.PATH; + HttpPost httppost = new HttpPost(url); + httppost.addHeader("Content-type", "application/x-www-form-urlencoded"); + httppost.addHeader("charset", "UTF-8"); + List nameValuePairs = new ArrayList<>(); + nameValuePairs.add(new BasicNameValuePair(SetWebhook.URL_FIELD, webHookURL)); + httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8")); + CloseableHttpResponse response = httpclient.execute(httppost); + HttpEntity ht = response.getEntity(); + + BufferedHttpEntity buf = new BufferedHttpEntity(ht); + String responseContent = EntityUtils.toString(buf, "UTF-8"); + + } catch (IOException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/org/telegram/api/Audio.java b/src/main/java/org/telegram/api/Audio.java new file mode 100644 index 0000000..92dc538 --- /dev/null +++ b/src/main/java/org/telegram/api/Audio.java @@ -0,0 +1,70 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents an audio file (voice note) + * @date 20 of June of 2015 + */ +public class Audio { + + public static final String FILEID_FIELD = "file_id"; + @JsonProperty(FILEID_FIELD) + private String fileId; ///< Unique identifier for this file + public static final String DURATION_FIELD = "duration"; + @JsonProperty(DURATION_FIELD) + private Integer duration; ///< Integer Duration of the audio in seconds as defined by sender + public static final String MIMETYPE_FIELD = "mime_type"; + @JsonProperty(MIMETYPE_FIELD) + private String mimeType; ///< Optional. MIME type of the file as defined by sender + public static final String FILESIZE_FIELD = "file_size"; + @JsonProperty(FILESIZE_FIELD) + private Integer fileSize; ///< Optional. File size + + public Audio() { + super(); + } + + public Audio(JSONObject jsonObject) { + super(); + this.fileId = jsonObject.getString(FILEID_FIELD); + this.duration = jsonObject.getInt(DURATION_FIELD); + this.mimeType = jsonObject.optString(MIMETYPE_FIELD, ""); + this.fileSize = jsonObject.optInt(FILESIZE_FIELD, 0); + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public Integer getDuration() { + return duration; + } + + public void setDuration(Integer duration) { + this.duration = duration; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } +} diff --git a/src/main/java/org/telegram/api/Chat.java b/src/main/java/org/telegram/api/Chat.java new file mode 100644 index 0000000..2c05947 --- /dev/null +++ b/src/main/java/org/telegram/api/Chat.java @@ -0,0 +1,73 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a Telegram chat with an user or a group + * @date 24 of June of 2015 + */ +public class Chat { + + public static final String ID_FIELD = "id"; + @JsonProperty(ID_FIELD) + private Integer id; ///< Unique identifier for this chat + public static final String FIRSTNAME_FIELD = "first_name"; + @JsonProperty(FIRSTNAME_FIELD) + private String firstName; ///< User‘s or bot’s first name + public static final String LASTNAME_FIELD = "last_name"; + @JsonProperty(LASTNAME_FIELD) + private String lastName; ///< Optional. User‘s or bot’s last name + public static final String USERNAME_FIELD = "username"; + @JsonProperty(USERNAME_FIELD) + private String userName; ///< Optional. User‘s or bot’s username + public static final String TITLE_FIELD = "title"; + @JsonProperty(TITLE_FIELD) + private String title; ///< Group name + + public Chat() { + super(); + } + + public Chat(JSONObject jsonObject) { + super(); + this.id = jsonObject.getInt(ID_FIELD); + if (this.id > 0) { + this.firstName = jsonObject.getString(FIRSTNAME_FIELD); + this.lastName = jsonObject.optString(LASTNAME_FIELD, ""); + this.userName = jsonObject.optString(USERNAME_FIELD, ""); + } else { + this.title = jsonObject.getString(TITLE_FIELD); + } + } + + public Integer getId() { + return id; + } + + public Boolean isGroupChat() { + if (id < 0) { + return true; + } else { + return false; + } + } + + public String getTitle() { + return title; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getUserName() { + return userName; + } +} diff --git a/src/main/java/org/telegram/api/Contact.java b/src/main/java/org/telegram/api/Contact.java new file mode 100644 index 0000000..8d7d4a5 --- /dev/null +++ b/src/main/java/org/telegram/api/Contact.java @@ -0,0 +1,38 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a phone contact. + * @date 20 of June of 2015 + */ +public class Contact { + + public static final String PHONENUMBER_FIELD = "phone_number"; + @JsonProperty(PHONENUMBER_FIELD) + private String phoneNumber; ///< Contact's phone number + public static final String FIRSTNAME_FIELD = "first_name"; + @JsonProperty(FIRSTNAME_FIELD) + private String firstName; ///< Contact's first name + public static final String LASTNAME_FIELD = "last_name"; + @JsonProperty(LASTNAME_FIELD) + private String lastName; ///< Optional. Contact's last name + public static final String USERID_FIELD = "user_id"; + @JsonProperty(USERID_FIELD) + private String userID; ///< Optional. Contact's user identifier in Telegram + + public Contact() { + super(); + } + + public Contact(JSONObject jsonObject) { + super(); + this.phoneNumber = jsonObject.getString(PHONENUMBER_FIELD); + this.firstName = jsonObject.getString(FIRSTNAME_FIELD); + this.lastName = jsonObject.optString(LASTNAME_FIELD, ""); + this.userID = jsonObject.optString(USERID_FIELD, ""); + } +} diff --git a/src/main/java/org/telegram/api/Document.java b/src/main/java/org/telegram/api/Document.java new file mode 100644 index 0000000..16f7735 --- /dev/null +++ b/src/main/java/org/telegram/api/Document.java @@ -0,0 +1,82 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a general file (as opposed to photos and audio files). + * Telegram users can send files of any type of up to 1.5 GB in size. + * @date 20 of June of 2015 + */ +public class Document { + + public static final String FILEID_FIELD = "file_id"; + @JsonProperty(FILEID_FIELD) + private String fileId; ///< Unique identifier for this file + public static final String THUMB_FIELD = "thumb"; + @JsonProperty(THUMB_FIELD) + private PhotoSize thumb; ///< Document thumbnail as defined by sender + public static final String FILENAME_FIELD = "file_name"; + @JsonProperty(FILENAME_FIELD) + private String fileName; ///< Optional. Original filename as defined by sender + public static final String MIMETYPE_FIELD = "mime_type"; + @JsonProperty(MIMETYPE_FIELD) + private String mimeType; ///< Optional. Mime type of a file as defined by sender + public static final String FILESIZE_FIELD = "file_size"; + @JsonProperty(FILESIZE_FIELD) + private Integer fileSize; ///< Optional. File size + + public Document() { + super(); + } + + public Document(JSONObject jsonObject) { + this.fileId = jsonObject.getString(FILEID_FIELD); + this.thumb = new PhotoSize(jsonObject.getJSONObject(THUMB_FIELD)); + this.fileName = jsonObject.optString(FILENAME_FIELD, ""); + this.mimeType = jsonObject.optString(MIMETYPE_FIELD, ""); + this.fileSize = jsonObject.optInt(FILESIZE_FIELD, 0); + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public PhotoSize getThumb() { + return thumb; + } + + public void setThumb(PhotoSize thumb) { + this.thumb = thumb; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } +} diff --git a/src/main/java/org/telegram/api/ForceReply.java b/src/main/java/org/telegram/api/ForceReply.java new file mode 100644 index 0000000..3143bfc --- /dev/null +++ b/src/main/java/org/telegram/api/ForceReply.java @@ -0,0 +1,71 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Upon receiving a message with this object, Telegram clients will display a reply interface to the user + * (act as if the user has selected the bot‘s message and tapped ’Reply'). + * This can be extremely useful if you want to create user-friendly step-by-step + * interfaces without having to sacrifice privacy mode. + * @date 22 of June of 2015 + */ +public class ForceReply implements ReplyKeyboard { + + public static final String FORCEREPLY_FIELD = "force_reply"; + /** + * Shows reply interface to the user, as if they manually selected the bot‘s message and tapped ’Reply' + */ + @JsonProperty(FORCEREPLY_FIELD) + private Boolean forceReply; + public static final String SELECTIVE_FIELD = "selective"; + /** + * Use this parameter if you want to force reply from specific users only. + * Targets: + * 1) users that are @mentioned in the text of the Message object; + * 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. + */ + @JsonProperty(SELECTIVE_FIELD) + private Boolean selective; + + public ForceReply() { + super(); + this.forceReply = true; + } + + public ForceReply(JSONObject jsonObject) { + super(); + this.forceReply = jsonObject.optBoolean(FORCEREPLY_FIELD, true); + this.selective = jsonObject.optBoolean(SELECTIVE_FIELD, false); + } + + public Boolean getForceReply() { + return forceReply; + } + + public void setForceReply(Boolean forceReply) { + this.forceReply = forceReply; + } + + public Boolean getSelective() { + return selective; + } + + public void setSelective(Boolean selective) { + this.selective = selective; + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + + jsonObject.put(FORCEREPLY_FIELD, this.forceReply); + if (this.selective != null) { + jsonObject.put(SELECTIVE_FIELD, this.selective); + } + + return jsonObject; + } +} diff --git a/src/main/java/org/telegram/api/Location.java b/src/main/java/org/telegram/api/Location.java new file mode 100644 index 0000000..42a5c3e --- /dev/null +++ b/src/main/java/org/telegram/api/Location.java @@ -0,0 +1,46 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a point on the map. + * @date 20 of June of 2015 + */ +public class Location { + + public static final String LONGITUDE_FIELD = "longitude"; + @JsonProperty(LONGITUDE_FIELD) + private Double longitude; ///< Longitude as defined by sender + public static final String LATITUDE_FIELD = "latitude"; + @JsonProperty(LATITUDE_FIELD) + private Double latitude; ///< Latitude as defined by sender + + public Location() { + super(); + } + + public Location(JSONObject jsonObject) { + super(); + this.longitude = jsonObject.getDouble(LONGITUDE_FIELD); + this.latitude = jsonObject.getDouble(LATITUDE_FIELD); + } + + public Double getLongitude() { + return longitude; + } + + public void setLongitude(Double longitude) { + this.longitude = longitude; + } + + public Double getLatitude() { + return latitude; + } + + public void setLatitude(Double latitude) { + this.latitude = latitude; + } +} diff --git a/src/main/java/org/telegram/api/Message.java b/src/main/java/org/telegram/api/Message.java new file mode 100644 index 0000000..acc2562 --- /dev/null +++ b/src/main/java/org/telegram/api/Message.java @@ -0,0 +1,339 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a message. + * @date 20 of June of 2015 + */ +public class Message { + public static final String MESSAGEID_FIELD = "message_id"; + @JsonProperty(MESSAGEID_FIELD) + private Integer messageId; ///< Integer Unique message identifier + public static final String FROM_FIELD = "from"; + @JsonProperty(FROM_FIELD) + private User from; ///< Sender + public static final String DATE_FIELD = "date"; + @JsonProperty(DATE_FIELD) + private Integer date; ///< Date the message was sent in Unix time + public static final String CHAT_FIELD = "chat"; + /** + * Conversation the message belongs to in case of a private message (@see User)or + * Conversation the message belongs to in case of a group (@see GroupChat) + */ + @JsonProperty(CHAT_FIELD) + private Chat chat; ///< Conversation the message belongs to in case of a private message + public static final String FORWARDFROM_FIELD = "forward_from"; + @JsonProperty(FORWARDFROM_FIELD) + private User forwardFrom; ///< Optional. For forwarded messages, sender of the original message + public static final String FORWARDDATE_FIELD = "forward_date"; + @JsonProperty(FORWARDDATE_FIELD) + private Integer forwardDate; ///< Optional. For forwarded messages, date the original message was sent + public static final String TEXT_FIELD = "text"; + @JsonProperty(TEXT_FIELD) + private String text; ///< Optional. For text messages, the actual UTF-8 text of the message + public static final String AUDIO_FIELD = "audio"; + @JsonProperty(AUDIO_FIELD) + private Audio audio; ///< Optional. Message is an audio file, information about the file + public static final String DOCUMENT_FIELD = "document"; + @JsonProperty(DOCUMENT_FIELD) + private Document document; ///< Optional. Message is a general file, information about the file + public static final String PHOTO_FIELD = "photo"; + @JsonProperty(PHOTO_FIELD) + private List photo; ///< Optional. Message is a photo, available sizes of the photo + public static final String STICKER_FIELD = "sticker"; + @JsonProperty(STICKER_FIELD) + private Sticker sticker; ///< Optional. Message is a sticker, information about the sticker + public static final String VIDEO_FIELD = "video"; + @JsonProperty(VIDEO_FIELD) + private Video video; ///< Optional. Message is a video, information about the video + public static final String CONTACT_FIELD = "contact"; + @JsonProperty(CONTACT_FIELD) + private Contact contact; ///< Optional. Message is a shared contact, information about the contact + public static final String LOCATION_FIELD = "location"; + @JsonProperty(LOCATION_FIELD) + private Location location; ///< Optional. Message is a shared location, information about the location + public static final String NEWCHATPARTICIPANT_FIELD = "new_chat_participant"; + @JsonProperty(NEWCHATPARTICIPANT_FIELD) + private User newChatParticipant; ///< Optional. A new member was added to the group, information about them (this member may be bot itself) + public static final String LEFTCHATPARTICIPANT_FIELD = "left_chat_participant"; + @JsonProperty(LEFTCHATPARTICIPANT_FIELD) + private User leftChatParticipant; ///< Optional. A member was removed from the group, information about them (this member may be bot itself) + public static final String NEWCHATTITLE_FIELD = "new_chat_title"; + @JsonProperty(NEWCHATTITLE_FIELD) + private String newChatTitle; ///< Optional. A group title was changed to this value + public static final String NEWCHATPHOTO_FIELD = "new_chat_photo"; + @JsonProperty(NEWCHATPHOTO_FIELD) + private String newChatPhoto; ///< Optional. A group photo was change to this value + public static final String DELETECHATPHOTO_FIELD = "delete_chat_photo"; + @JsonProperty(DELETECHATPHOTO_FIELD) + private Boolean deleteChatPhoto; ///< Optional. Informs that the group photo was deleted + public static final String GROUPCHATCREATED_FIELD = "group_chat_created"; + @JsonProperty(GROUPCHATCREATED_FIELD) + private Boolean groupchatCreated; ///< Optional. Informs that the group has been created + public static final String REPLYTOMESSAGE_FIELD = "reply_to_message"; + @JsonProperty(REPLYTOMESSAGE_FIELD) + private Message replyToMessage; + + public Message() { + super(); + } + + public Message(JSONObject jsonObject) { + super(); + this.messageId = jsonObject.getInt(MESSAGEID_FIELD); + this.from = new User(jsonObject.getJSONObject(FROM_FIELD)); + this.date = jsonObject.getInt(DATE_FIELD); + this.chat = new Chat(jsonObject.getJSONObject(CHAT_FIELD)); + if (jsonObject.has(FORWARDFROM_FIELD)) { + this.forwardFrom = new User(jsonObject.getJSONObject(FORWARDFROM_FIELD)); + } + if (jsonObject.has(FORWARDDATE_FIELD)) { + this.forwardDate = jsonObject.getInt(FORWARDDATE_FIELD); + } + if (jsonObject.has(TEXT_FIELD)) { + this.text = jsonObject.getString(TEXT_FIELD); + } + if (jsonObject.has(AUDIO_FIELD)) { + this.audio = new Audio(jsonObject.getJSONObject(AUDIO_FIELD)); + } + if (jsonObject.has(DOCUMENT_FIELD)) { + this.document = new Document(jsonObject.getJSONObject(DOCUMENT_FIELD)); + } + this.photo = new ArrayList(); + if (jsonObject.has(PHOTO_FIELD)) { + JSONArray photos = jsonObject.getJSONArray(PHOTO_FIELD); + for (int i = 0; i < photos.length(); i++) { + this.photo.add(new PhotoSize(photos.getJSONObject(i))); + } + } + if (jsonObject.has(STICKER_FIELD)) { + this.sticker = new Sticker(jsonObject.getJSONObject(STICKER_FIELD)); + } + if (jsonObject.has(VIDEO_FIELD)) { + this.video = new Video(jsonObject.getJSONObject(VIDEO_FIELD)); + } + if (jsonObject.has(CONTACT_FIELD)) { + this.contact = new Contact(jsonObject.getJSONObject(CONTACT_FIELD)); + } + if (jsonObject.has(LOCATION_FIELD)) { + this.location = new Location(jsonObject.getJSONObject(LOCATION_FIELD)); + } + if (jsonObject.has(NEWCHATPARTICIPANT_FIELD)) { + this.newChatParticipant = new User(jsonObject.getJSONObject(NEWCHATPARTICIPANT_FIELD)); + } + if (jsonObject.has(LEFTCHATPARTICIPANT_FIELD)) { + this.leftChatParticipant = new User(jsonObject.getJSONObject(LEFTCHATPARTICIPANT_FIELD)); + } + if (jsonObject.has(REPLYTOMESSAGE_FIELD)) { + this.replyToMessage = new Message(jsonObject.getJSONObject(REPLYTOMESSAGE_FIELD)); + } + this.newChatTitle = jsonObject.optString(NEWCHATTITLE_FIELD, ""); + this.newChatPhoto = jsonObject.optString(NEWCHATPHOTO_FIELD, ""); + this.deleteChatPhoto = jsonObject.optBoolean(DELETECHATPHOTO_FIELD, false); + this.groupchatCreated = jsonObject.optBoolean(GROUPCHATCREATED_FIELD, false); + } + + public Integer getMessageId() { + return messageId; + } + + public void setMessageId(Integer messageId) { + this.messageId = messageId; + } + + public User getFrom() { + return from; + } + + public void setFrom(User from) { + this.from = from; + } + + public Integer getDate() { + return date; + } + + public void setDate(Integer date) { + this.date = date; + } + + public boolean isGroupMessage() { + return chat.isGroupChat(); + } + + public Integer getChatId() { + return chat.getId(); + } + + public Chat getChat() { + return chat; + } + + public void setChat(Chat chat) { + this.chat = chat; + } + + public User getForwardFrom() { + return forwardFrom; + } + + public void setForwardFrom(User forwardFrom) { + this.forwardFrom = forwardFrom; + } + + public Integer getForwardDate() { + return forwardDate; + } + + public void setForwardDate(Integer forwardDate) { + this.forwardDate = forwardDate; + } + + public boolean hasText() { + return text != null; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Audio getAudio() { + return audio; + } + + public void setAudio(Audio audio) { + this.audio = audio; + } + + public boolean hasDocument() { + return this.document != null; + } + + public Document getDocument() { + return document; + } + + public void setDocument(Document document) { + this.document = document; + } + + public List getPhoto() { + return photo; + } + + public void setPhoto(List photo) { + this.photo = photo; + } + + public Sticker getSticker() { + return sticker; + } + + public void setSticker(Sticker sticker) { + this.sticker = sticker; + } + + public Video getVideo() { + return video; + } + + public void setVideo(Video video) { + this.video = video; + } + + public Contact getContact() { + return contact; + } + + public void setContact(Contact contact) { + this.contact = contact; + } + + public Location getLocation() { + return location; + } + + public void setLocation(Location location) { + this.location = location; + } + + public User getNewChatParticipant() { + return newChatParticipant; + } + + public void setNewChatParticipant(User newChatParticipant) { + this.newChatParticipant = newChatParticipant; + } + + public User getLeftChatParticipant() { + return leftChatParticipant; + } + + public void setLeftChatParticipant(User leftChatParticipant) { + this.leftChatParticipant = leftChatParticipant; + } + + public String getNewChatTitle() { + return newChatTitle; + } + + public void setNewChatTitle(String newChatTitle) { + this.newChatTitle = newChatTitle; + } + + public String getNewChatPhoto() { + return newChatPhoto; + } + + public void setNewChatPhoto(String newChatPhoto) { + this.newChatPhoto = newChatPhoto; + } + + public Boolean getDeleteChatPhoto() { + return deleteChatPhoto; + } + + public void setDeleteChatPhoto(Boolean deleteChatPhoto) { + this.deleteChatPhoto = deleteChatPhoto; + } + + public Boolean getGroupchatCreated() { + return groupchatCreated; + } + + public void setGroupchatCreated(Boolean groupchatCreated) { + this.groupchatCreated = groupchatCreated; + } + + public boolean hasReplayMessage() { + return replyToMessage != null; + } + + public Message getReplyToMessage() { + return replyToMessage; + } + + public void setReplyToMessage(Message replyToMessage) { + this.replyToMessage = replyToMessage; + } + + public boolean isReply() { + return this.replyToMessage != null; + } + + public boolean hasLocation() { + return location != null; + } +} diff --git a/src/main/java/org/telegram/api/PhotoSize.java b/src/main/java/org/telegram/api/PhotoSize.java new file mode 100644 index 0000000..293faa9 --- /dev/null +++ b/src/main/java/org/telegram/api/PhotoSize.java @@ -0,0 +1,70 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents one size of a photo or a file / sticker thumbnail. + * @date 20 of June of 2015 + */ +public class PhotoSize { + + public static final String FILEID_FIELD = "file_id"; + @JsonProperty(FILEID_FIELD) + private String fileId; ///< Unique identifier for this file + public static final String WIDTH_FIELD = "width"; + @JsonProperty(WIDTH_FIELD) + private Integer width; ///< Photo width + public static final String HEIGHT_FIELD = "height"; + @JsonProperty(HEIGHT_FIELD) + private Integer height; ///< Photo height + public static final String FILESIZE_FIELD = "file_size"; + @JsonProperty(FILESIZE_FIELD) + private Integer fileSize; ///< Optional. File size + + public PhotoSize() { + super(); + } + + public PhotoSize(JSONObject jsonObject) { + super(); + this.fileId = jsonObject.optString(FILEID_FIELD, ""); + this.width = jsonObject.optInt(WIDTH_FIELD, 0); + this.height = jsonObject.optInt(HEIGHT_FIELD, 0); + this.fileSize = jsonObject.optInt(FILESIZE_FIELD, 0); + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } +} diff --git a/src/main/java/org/telegram/api/ReplyKeyboard.java b/src/main/java/org/telegram/api/ReplyKeyboard.java new file mode 100644 index 0000000..d19efbf --- /dev/null +++ b/src/main/java/org/telegram/api/ReplyKeyboard.java @@ -0,0 +1,13 @@ +package org.telegram.api; + +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Reply keyboard abstract type + * @date 20 of June of 2015 + */ +public interface ReplyKeyboard { + JSONObject toJson(); +} diff --git a/src/main/java/org/telegram/api/ReplyKeyboardHide.java b/src/main/java/org/telegram/api/ReplyKeyboardHide.java new file mode 100644 index 0000000..76afe22 --- /dev/null +++ b/src/main/java/org/telegram/api/ReplyKeyboardHide.java @@ -0,0 +1,66 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Upon receiving a message with this object, + * Telegram clients will hide the current custom keyboard and display the default letter-keyboard. + * By default, custom keyboards are displayed until a new keyboard is sent by a bot. + * An exception is made for one-time keyboards that are hidden immediately after the user presses a button + * (@see ReplyKeyboardMarkup). + * @date 20 of June of 2015 + */ +public class ReplyKeyboardHide implements ReplyKeyboard{ + + public static final String HIDEKEYBOARD_FIELD = "hide_keyboard"; + @JsonProperty(HIDEKEYBOARD_FIELD) + private Boolean hideKeyboard; ///< Requests clients to hide the custom keyboard + public static final String SELECTIVE_FIELD = "selective"; + /** + * Optional. Use this parameter if you want to show the keyboard to specific users only. + * Targets: + * 1) users that are @mentioned in the text of the Message object; + * 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. + */ + @JsonProperty(SELECTIVE_FIELD) + private Boolean selective; + + public ReplyKeyboardHide() { + super(); + this.selective = true; + } + + public ReplyKeyboardHide(JSONObject jsonObject) { + super(); + this.hideKeyboard = jsonObject.optBoolean(HIDEKEYBOARD_FIELD, true); + this.selective = jsonObject.optBoolean(SELECTIVE_FIELD, true); + } + + public Boolean getHideKeyboard() { + return hideKeyboard; + } + + public void setHideKeyboard(Boolean hideKeyboard) { + this.hideKeyboard = hideKeyboard; + } + + public Boolean getSelective() { + return selective; + } + + public void setSelective(Boolean selective) { + this.selective = selective; + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(HIDEKEYBOARD_FIELD, this.hideKeyboard); + jsonObject.put(SELECTIVE_FIELD, this.selective); + return jsonObject; + } + +} diff --git a/src/main/java/org/telegram/api/ReplyKeyboardMarkup.java b/src/main/java/org/telegram/api/ReplyKeyboardMarkup.java new file mode 100644 index 0000000..c5aafee --- /dev/null +++ b/src/main/java/org/telegram/api/ReplyKeyboardMarkup.java @@ -0,0 +1,116 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a custom keyboard with reply options. + * @date 20 of June of 2015 + */ +public class ReplyKeyboardMarkup implements ReplyKeyboard { + + public static final String KEYBOARD_FIELD = "keyboard"; + @JsonProperty(KEYBOARD_FIELD) + private List> keyboard; ///< Array of button rows, each represented by an Array of Strings + public static final String RESIZEKEYBOARD_FIELD = "resize_keyboard"; + @JsonProperty(RESIZEKEYBOARD_FIELD) + private Boolean resizeKeyboard; ///< Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false. + public static final String ONETIMEKEYBOARD_FIELD = "one_time_keyboard"; + @JsonProperty(ONETIMEKEYBOARD_FIELD) + private Boolean oneTimeKeyboad; ///< Optional. Requests clients to hide the keyboard as soon as it's been used. Defaults to false. + public static final String SELECTIVE_FIELD = "selective"; + /** + * Optional. Use this parameter if you want to show the keyboard to specific users only. + * Targets: + * 1) users that are @mentioned in the text of the Message object; + * 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. + */ + private Boolean selective; + + public ReplyKeyboardMarkup() { + super(); + keyboard = new ArrayList>(); + } + + public ReplyKeyboardMarkup(JSONObject jsonObject) { + super(); + this.keyboard = new ArrayList>(); + JSONArray keyboard = jsonObject.getJSONArray(KEYBOARD_FIELD); + for (int i=0; i< keyboard.length(); i++) { + JSONArray keyboardRow = keyboard.getJSONArray(i); + List row = new ArrayList(); + for (int j=0; j < keyboardRow.length(); j++) { + row.add(keyboardRow.getString(j)); + } + this.keyboard.add(row); + } + this.resizeKeyboard = jsonObject.optBoolean(RESIZEKEYBOARD_FIELD, false); + this.oneTimeKeyboad = jsonObject.optBoolean(ONETIMEKEYBOARD_FIELD, false); + this.selective = jsonObject.optBoolean(SELECTIVE_FIELD, false); + } + + public List> getKeyboard() { + return keyboard; + } + + public void setKeyboard(List> keyboard) { + this.keyboard = keyboard; + } + + public Boolean getResizeKeyboard() { + return resizeKeyboard; + } + + public void setResizeKeyboard(Boolean resizeKeyboard) { + this.resizeKeyboard = resizeKeyboard; + } + + public Boolean getOneTimeKeyboad() { + return oneTimeKeyboad; + } + + public void setOneTimeKeyboad(Boolean oneTimeKeyboad) { + this.oneTimeKeyboad = oneTimeKeyboad; + } + + public Boolean getSelective() { + return selective; + } + + public void setSelective(Boolean selective) { + this.selective = selective; + } + + @Override + public JSONObject toJson() { + JSONObject jsonObject = new JSONObject(); + JSONArray jsonkeyboard = new JSONArray(); + + for (List innerRow : this.keyboard) { + JSONArray innerJSONKeyboard = new JSONArray(); + for (String element: innerRow) { + innerJSONKeyboard.put(element); + } + jsonkeyboard.put(innerJSONKeyboard); + } + jsonObject.put(ReplyKeyboardMarkup.KEYBOARD_FIELD, jsonkeyboard); + + if (this.oneTimeKeyboad != null) { + jsonObject.put(ReplyKeyboardMarkup.ONETIMEKEYBOARD_FIELD, this.oneTimeKeyboad); + } + if (this.resizeKeyboard != null) { + jsonObject.put(ReplyKeyboardMarkup.RESIZEKEYBOARD_FIELD, this.resizeKeyboard); + } + if (this.selective != null) { + jsonObject.put(ReplyKeyboardMarkup.SELECTIVE_FIELD, this.selective); + } + + return jsonObject; + } +} diff --git a/src/main/java/org/telegram/api/Sticker.java b/src/main/java/org/telegram/api/Sticker.java new file mode 100644 index 0000000..781794c --- /dev/null +++ b/src/main/java/org/telegram/api/Sticker.java @@ -0,0 +1,42 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a sticker. + * @date 20 of June of 2015 + */ +public class Sticker { + + public static final String FILEID_FIELD = "file_id"; + @JsonProperty(FILEID_FIELD) + private String fileId; ///< Unique identifier for this file + public static final String WIDTH_FIELD = "width"; + @JsonProperty(WIDTH_FIELD) + private Integer width; ///< Sticker width + public static final String HEIGHT_FIELD = "height"; + @JsonProperty(HEIGHT_FIELD) + private Integer height; ///< Sticker height + public static final String THUMB_FIELD = "thumb"; + @JsonProperty(THUMB_FIELD) + private PhotoSize thumb; ///< Sticker thumbnail in .webp or .jpg format + public static final String FILESIZE_FIELD = "file_size"; + @JsonProperty(FILESIZE_FIELD) + private Integer fileSize; ///< Optional. File size + + public Sticker() { + super(); + } + + public Sticker(JSONObject jsonObject) { + super(); + this.fileId = jsonObject.getString(FILEID_FIELD); + this.width = jsonObject.getInt(WIDTH_FIELD); + this.height = jsonObject.getInt(HEIGHT_FIELD); + this.thumb = new PhotoSize(jsonObject.getJSONObject(THUMB_FIELD)); + this.fileSize = jsonObject.optInt(FILESIZE_FIELD, 0); + } +} diff --git a/src/main/java/org/telegram/api/Update.java b/src/main/java/org/telegram/api/Update.java new file mode 100644 index 0000000..5f88d6c --- /dev/null +++ b/src/main/java/org/telegram/api/Update.java @@ -0,0 +1,55 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import org.json.JSONObject; +import sun.print.UnixPrintJob; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief TODO + * @date 20 of June of 2015 + */ +public class Update { + /* + update_id Integer +message Message + */ + public static final String UPDATEID_FIELD = "update_id"; + /** + * The update‘s unique identifier. + * Update identifiers start from a certain positive number and increase sequentially. + * This ID becomes especially handy if you’re using Webhooks, + * since it allows you to ignore repeated updates or to restore the + * correct update sequence, should they get out of order. + */ + @JsonProperty(UPDATEID_FIELD) + private Integer updateId; + public static final String MESSAGE_FIELD = "message"; + @JsonProperty(MESSAGE_FIELD) + private Message message; ///< Optional. New incoming message of any kind — text, photo, sticker, etc. + + public Update() { + super(); + } + + public Update(JSONObject jsonObject) { + super(); + this.updateId = jsonObject.getInt(UPDATEID_FIELD); + JSONObject messageJSON = jsonObject.optJSONObject(MESSAGE_FIELD); + if (messageJSON == null) { + this.message = null; + } else { + this.message = new Message(messageJSON); + } + } + + public Integer getUpdateId() { + return updateId; + } + + public Message getMessage() { + return message; + } +} diff --git a/src/main/java/org/telegram/api/User.java b/src/main/java/org/telegram/api/User.java new file mode 100644 index 0000000..3af2788 --- /dev/null +++ b/src/main/java/org/telegram/api/User.java @@ -0,0 +1,54 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a Telegram user or bot. + * @date 20 of June of 2015 + */ +public class User { + + public static final String ID_FIELD = "id"; + @JsonProperty(ID_FIELD) + private Integer id; ///< Unique identifier for this user or bot + public static final String FIRSTNAME_FIELD = "first_name"; + @JsonProperty(FIRSTNAME_FIELD) + private String firstName; ///< User‘s or bot’s first name + public static final String LASTNAME_FIELD = "last_name"; + @JsonProperty(LASTNAME_FIELD) + private String lastName; ///< Optional. User‘s or bot’s last name + public static final String USERNAME_FIELD = "username"; + @JsonProperty(USERNAME_FIELD) + private String userName; ///< Optional. User‘s or bot’s username + + public User() { + super(); + } + + public User(JSONObject jsonObject) { + super(); + this.id = jsonObject.getInt(ID_FIELD); + this.firstName = jsonObject.getString(FIRSTNAME_FIELD); + this.lastName = jsonObject.optString(LASTNAME_FIELD, ""); + this.userName = jsonObject.optString(USERNAME_FIELD, ""); + } + + public Integer getId() { + return id; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getUserName() { + return userName; + } +} diff --git a/src/main/java/org/telegram/api/UserProfilePhotos.java b/src/main/java/org/telegram/api/UserProfilePhotos.java new file mode 100644 index 0000000..a3b2ca1 --- /dev/null +++ b/src/main/java/org/telegram/api/UserProfilePhotos.java @@ -0,0 +1,43 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represent a user's profile pictures. + * @date 22 of June of 2015 + */ +public class UserProfilePhotos { + + public static final String TOTALCOUNT_FIELD = "total_count"; + @JsonProperty(TOTALCOUNT_FIELD) + private Integer totalCount; ///< Total number of profile pictures the target user has + public static final String PHOTOS_FIELD = "photos"; + @JsonProperty(PHOTOS_FIELD) + private List> photos; ///< Requested profile pictures (in up to 4 sizes each) + + public UserProfilePhotos() { + super(); + } + + public UserProfilePhotos(JSONObject jsonObject) { + super(); + this.totalCount = jsonObject.getInt(TOTALCOUNT_FIELD); + this.photos = new ArrayList<>(); + JSONArray photos = jsonObject.getJSONArray(PHOTOS_FIELD); + for (int i = 0; i < photos.length(); i++) { + JSONArray innerArray = photos.getJSONArray(i); + List innerPhotos = new ArrayList<>(); + for (int j = 0; j < innerArray.length(); j ++) { + innerPhotos.add(new PhotoSize(innerArray.getJSONObject(j))); + } + this.photos.add(innerPhotos); + } + } +} diff --git a/src/main/java/org/telegram/api/Video.java b/src/main/java/org/telegram/api/Video.java new file mode 100644 index 0000000..5b826b0 --- /dev/null +++ b/src/main/java/org/telegram/api/Video.java @@ -0,0 +1,117 @@ +package org.telegram.api; + +import com.fasterxml.jackson.annotation.JsonProperty; +import org.json.JSONObject; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief This object represents a video file. + * @date 20 of June of 2015 + */ +public class Video { + + public static final String FILEID_FIELD = "file_id"; + @JsonProperty(FILEID_FIELD) + private String fileId; ///< Unique identifier for this file + public static final String WIDTH_FIELD = "width"; + @JsonProperty(WIDTH_FIELD) + private Integer width; ///< Video width as defined by sender + public static final String HEIGHT_FIELD = "height"; + @JsonProperty(HEIGHT_FIELD) + private Integer height; ///< Video height as defined by sender + public static final String DURATION_FIELD = "duration"; + @JsonProperty(DURATION_FIELD) + private Integer duration; ///< Duration of the video in seconds as defined by sender + public static final String THUMB_FIELD = "thumb"; + @JsonProperty(THUMB_FIELD) + private PhotoSize thumb; ///< Video thumbnail + public static final String MIMETYPE_FIELD = "mime_type"; + @JsonProperty(MIMETYPE_FIELD) + private String mimeType; ///< Optional. Mime type of a file as defined by sender + public static final String FILESIZE_FIELD = "file_size"; + @JsonProperty(FILESIZE_FIELD) + private Integer fileSize; ///< Optional. File size + public static final String CAPTION_FIELD = "caption"; + @JsonProperty(CAPTION_FIELD) + private String caption; ///< Optional. Text description of the video (usually empty) + + public Video() { + super(); + } + + public Video(JSONObject jsonObject) { + this.fileId = jsonObject.getString(FILEID_FIELD); + this.width = jsonObject.getInt(WIDTH_FIELD); + this.height = jsonObject.getInt(HEIGHT_FIELD); + this.duration = jsonObject.getInt(DURATION_FIELD); + this.thumb = new PhotoSize(jsonObject.getJSONObject(THUMB_FIELD)); + this.mimeType = jsonObject.optString(MIMETYPE_FIELD, ""); + this.fileSize = jsonObject.optInt(FILESIZE_FIELD, 0); + this.caption = jsonObject.optString(CAPTION_FIELD, ""); + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public Integer getDuration() { + return duration; + } + + public void setDuration(Integer duration) { + this.duration = duration; + } + + public PhotoSize getThumb() { + return thumb; + } + + public void setThumb(PhotoSize thumb) { + this.thumb = thumb; + } + + public String getMimeType() { + return mimeType; + } + + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + public Integer getFileSize() { + return fileSize; + } + + public void setFileSize(Integer fileSize) { + this.fileSize = fileSize; + } + + public String getCaption() { + return caption; + } + + public void setCaption(String caption) { + this.caption = caption; + } +} diff --git a/src/main/java/org/telegram/database/ConectionDB.java b/src/main/java/org/telegram/database/ConectionDB.java new file mode 100644 index 0000000..fa75142 --- /dev/null +++ b/src/main/java/org/telegram/database/ConectionDB.java @@ -0,0 +1,112 @@ +/* + * This is the source code of Telegram Bot v. 2.0 + * It is licensed under GNU GPL v. 3 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Ruben Bermudez, 3/12/14. + */ +package org.telegram.database; + +import org.telegram.BuildVars; +import org.telegram.services.BotLogger; + +import java.sql.*; + +/** + * @author Ruben Bermudez + * @version 2.0 + * @brief Connector to database + * @date 3/12/14 + */ +public class ConectionDB { + private static volatile BotLogger log = BotLogger.getLogger(ConectionDB.class.getName()); + private Connection currentConection; + + public ConectionDB() { + this.currentConection = openConexion(); + } + + private Connection openConexion() { + Connection connection = null; + try { + Class.forName(BuildVars.controllerDB).newInstance(); + connection = DriverManager.getConnection(BuildVars.linkDB, BuildVars.userDB, BuildVars.password); + } catch (SQLException | ClassNotFoundException | IllegalAccessException | InstantiationException e) { + log.error(e); + } + + return connection; + } + + public void closeConexion() { + try { + this.currentConection.close(); + } catch (SQLException e) { + log.error(e); + } + + } + + public ResultSet runSqlQuery(String query) throws SQLException { + final Statement statement; + statement = this.currentConection.createStatement(); + return statement.executeQuery(query); + } + + public Boolean executeQuery(String query) throws SQLException { + final Statement statement = this.currentConection.createStatement(); + return statement.execute(query); + } + + public PreparedStatement getPreparedStatement(String query) throws SQLException { + return this.currentConection.prepareStatement(query); + } + + public PreparedStatement getPreparedStatement(String query, int flags) throws SQLException { + return this.currentConection.prepareStatement(query, flags); + } + + public int checkVersion() { + int max = 0; + try { + final DatabaseMetaData metaData = this.currentConection.getMetaData(); + final ResultSet res = metaData.getTables(null, null, null, + new String[]{"TABLE"}); + while (res.next()) { + if (res.getString("TABLE_NAME").compareTo("Versions") == 0) { + final ResultSet result = runSqlQuery("SELECT Max(Version) FROM Versions"); + while (result.next()) { + max = (max > result.getInt(1)) ? max : result.getInt(1); + } + } + } + } catch (SQLException e) { + log.error(e); + } + return max; + } + + /** + * Initilize a transaction in database + * @throws SQLException If initialization fails + */ + public void initTransaction() throws SQLException { + this.currentConection.setAutoCommit(false); + } + + /** + * Finish a transaction in database and commit changes + * @throws SQLException If a rollback fails + */ + public void commitTransaction() throws SQLException { + try { + this.currentConection.commit(); + } catch (SQLException e) { + if (this.currentConection != null) { + this.currentConection.rollback(); + } + } finally { + this.currentConection.setAutoCommit(false); + } + } +} diff --git a/src/main/java/org/telegram/database/CreationStrings.java b/src/main/java/org/telegram/database/CreationStrings.java new file mode 100644 index 0000000..5fb2621 --- /dev/null +++ b/src/main/java/org/telegram/database/CreationStrings.java @@ -0,0 +1,93 @@ +package org.telegram.database; + +/** + * @author Ruben Bermudez + * @version 2.0 + * @brief Strings to create database + * @date 15 of May of 2015 + */ +public class CreationStrings { + public static final int version = 3; + public static final String createVersionTable = "CREATE TABLE IF NOT EXISTS Versions(ID INTEGER PRIMARY KEY AUTO_INCREMENT, Version INTEGER);"; + public static final String insertCurrentVersion = "INSERT IGNORE INTO Versions (Version) VALUES(" + version + ");"; + public static final String createFilesTable = "CREATE TABLE IF NOT EXISTS Files (fileId VARCHAR(100) PRIMARY KEY, userId INTEGER NOT NULL, caption TEXT NOT NULL)"; + public static final String createUsersForFilesTable = "CREATE TABLE IF NOT EXISTS FilesUsers (userId INTEGER PRIMARY KEY, status INTEGER NOT NULL DEFAULT 0)"; + public static final String createRecentWeatherTable = "CREATE TABLE IF NOT EXISTS RecentWeather (ID INTEGER PRIMARY KEY AUTO_INCREMENT, userId INTEGER NOT NULL, " + + "date TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, cityId INTEGER NOT NULL, cityName VARCHAR(60) NOT NULL," + + "CONSTRAINT unique_cistyuser UNIQUE (userId,cityId))"; + public static final String createDirectionsDatabase = "CREATE TABLE IF NOT EXISTS Directions (userId INTEGER PRIMARY KEY, status INTEGER NOT NULL, " + + "messageId INTEGER NOT NULL DEFAULT 0, origin VARCHAR(100));"; + + + /*public static final String createOperatingSystemsVersion = "CREATE TABLE IF NOT EXISTS OperatingSystems (operatingSystem VARCHAR(20) PRIMARY KEY);"; + public static final String createLanguagesTable = "CREATE TABLE IF NOT EXISTS Languages(languageCode VARCHAR(6) PRIMARY KEY, languageName VARCHAR(30) NOT NULL);"; + public static final String insertEnglishLanguage = "INSERT IGNORE INTO Languages (languageCode, languageName) VALUES('en', 'English');"; + public static final String insertPortugueseLanguage = "INSERT IGNORE INTO Languages (languageCode, languageName) VALUES('pt', 'Portuguese');"; + public static final String insertItalianLanguage = "INSERT IGNORE INTO Languages (languageCode, languageName) VALUES('it', 'Italian');"; + public static final String createCountriesTable = "CREATE TABLE IF NOT EXISTS Countries(countryCode VARCHAR(6) PRIMARY KEY, name VARCHAR(20) NOT NULL);"; + public static final String createLoginTable = "CREATE TABLE IF NOT EXISTS Logins(username VARCHAR(30) PRIMARY KEY, password VARCHAR(30) NOT NULL, " + + "hash VARCHAR(130) NOT NULL, securityLevel INTEGER DEFAULT 0);"; + public static final String createLanguagesPerCountryTable = "CREATE TABLE IF NOT EXISTS LanguagesPerCountry( countryCode VARCHAR(6) PRIMARY KEY, firstLanguage VARCHAR(6) NOT NULL, " + + "secondLanguage VARCHAR(6) DEFAULT NULL, FOREIGN KEY (firstLanguage) REFERENCES Languages (languageCode) ON UPDATE CASCADE ON DELETE CASCADE, " + + "FOREIGN KEY (secondLanguage) REFERENCES Languages (languageCode) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String createUsersTable = "CREATE TABLE IF NOT EXISTS Users(userId INTEGER NOT NULL, bot INTEGER NOT NULL, needVolunteer BOOLEAN NOT NULL, phone VARCHAR(15) DEFAULT NULL, firstName VARCHAR(100) NOT NULL, " + + "lastName VARCHAR(100) NOT NULL, userName VARCHAR(100) DEFAULT NULL, isContact BOOLEAN NOT NULL, lastAnswer DATETIME, userHash VARCHAR(40) NOT NULL, lastScore FLOAT, hashtag VARCHAR(15), countryCode VARCHAR(6), denied INTEGER NOT NULL DEFAULT 0, " + + "customCommand VARCHAR(60) DEFAULT NULL, deleted BOOLEAN NOT NULL DEFAULT FALSE, " + + "CONSTRAINT `usersCountryCode` FOREIGN KEY (countryCode) REFERENCES Countries(countryCode) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `userPrimaryKey` PRIMARY KEY(userId,bot));"; + public static final String createAnswersTable = "CREATE TABLE IF NOT EXISTS AnswersXXXX (ID INTEGER PRIMARY KEY AUTO_INCREMENT, name VARCHAR(250) NOT NULL," + + "content VARCHAR(2000) NOT NULL, operatingSystem VARCHAR(20) NOT NULL, appVersion VARCHAR(10)," + + "CONSTRAINT `answersOSXXXX` FOREIGN KEY (operatingSystem) REFERENCES OperatingSystems(operatingSystem) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String insertEmptyAnswer = "INSERT IGNORE INTO AnswersXXXX (name, content, operatingSystem, appVersion, ID) VALUES ('Empty Answer','','All','',100);"; + public static final String insertNotFoundAnswer = "INSERT IGNORE INTO AnswersXXXX (name, content, operatingSystem, appVersion, ID) VALUES ('Not found','','All','',99);"; + public static final String createHistoryTable = "CREATE TABLE IF NOT EXISTS History( ID INTEGER PRIMARY KEY AUTO_INCREMENT, userId INTEGER NOT NULL, date DATETIME NOT NULL, answerId INTEGER NOT NULL, " + + "languageCode VARCHAR(6) NOT NULL, CONSTRAINT `historyUserId` FOREIGN KEY (userId) REFERENCES Users(userId) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `historyLanguageCode` FOREIGN KEY (languageCode) REFERENCES Languages(languageCode) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String createQuestionsTable = "CREATE TABLE IF NOT EXISTS QuestionsXXXX (ID INTEGER PRIMARY KEY AUTO_INCREMENT, content VARCHAR(300) NOT NULL, " + + "languageCode VARCHAR(6) NOT NULL, answerAll INTEGER DEFAULT NULL, answerIos INTEGER DEFAULT NULL, answerWebogram INTEGER DEFAULT NULL, answerAndroid INTEGER DEFAULT NULL, " + + "answerOsx INTEGER DEFAULT NULL, answerDesktop INTEGER DEFAULT NULL, answerWP INTEGER DEFAULT NULL, " + + "CONSTRAINT `questionsAnswerAllXXXX` FOREIGN KEY (answerAll) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerIosXXXX` FOREIGN KEY (answerIos) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerWebogramXXXX` FOREIGN KEY (answerWebogram) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerAndroidXXXX` FOREIGN KEY (answerAndroid) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerOsxXXXX` FOREIGN KEY (answerOsx) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerDesktopXXXX` FOREIGN KEY (answerDesktop) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `questionsAnswerWPXXXX` FOREIGN KEY (answerWP) REFERENCES AnswersXXXX(ID) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String createTrelloTable = "CREATE TABLE IF NOT EXISTS trello (shortLink VARCHAR(10) PRIMARY KEY NOT NULL, listName VARCHAR(20) NOT NULL, listId VARCHAR(30), " + + "closed BOOLEAN NOT NULL, creationDate DATETIME NOT NULL, notificationDate DATETIME NOT NULL, affectedUserCount INTEGER NOT NULL DEFAULT 0, tittle VARCHAR(100) NOT NULL, " + + "description TEXT NOT NULL);"; + public static final String createTemplatesTable = "CREATE TABLE IF NOT EXISTS Templates (id INTEGER PRIMARY KEY AUTO_INCREMENT, hash VARCHAR(130) NOT NULL, " + + "languageCode VARCHAR(8), content TEXT);"; + public static final String createTemplatesLogTable = "CREATE TABLE IF NOT EXISTS TemplatesLog (id INTEGER PRIMARY KEY AUTO_INCREMENT, hash VARCHAR(130) NOT NULL, type INTEGER, content TEXT);"; + public static final String createTemplatesFileTable = "CREATE TABLE IF NOT EXISTS TemplatesFile (id INTEGER PRIMARY KEY AUTO_INCREMENT, hash VARCHAR(130) NOT NULL, date DATETIME, languageCode VARCHAR(8)," + + " CONSTRAINT unique_filelanguage UNIQUE (hash,languageCode));"; + public static final String createFiltersTable = "create table if not exists Filters (filterId INTEGER PRIMARY KEY AUTO_INCREMENT," + + " words VARCHAR(300) NOT NULL, languageCode VARCHAR(6) NOT NULL," + + " CONSTRAINT `filtersLanguageCode` FOREIGN KEY (languageCode) REFERENCES Languages(languageCode) ON UPDATE CASCADE ON DELETE CASCADE" + + ");"; + public static final String createMessagesDiscardedTable = "create table if not exists MessagesDiscarded (id INTEGER PRIMARY KEY AUTO_INCREMENT," + + " userId INTEGER NOT NULL, filterId INTEGER NOT NULL, message TEXT NOT NULL, date TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," + + " partial BOOLEAN NOT NULL DEFAULT false, CONSTRAINT `messagesDiscardedUserId` FOREIGN KEY (userId) REFERENCES Users(userId) ON UPDATE CASCADE ON DELETE CASCADE," + + " CONSTRAINT `messageDiscardedFilterId` FOREIGN KEY (filterId) REFERENCES Filters(filterId) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String createMessagesAnsweredTable = "create table if not exists MessagesAnswered (id INTEGER PRIMARY KEY AUTO_INCREMENT," + + " userId INTEGER NOT NULL, answer INTEGER NOT NULL, message TEXT NOT NULL, matchValue FLOAT NOT NULL, date TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," + + " partial BOOLEAN NOT NULL DEFAULT false, CONSTRAINT `messagesAnswerdUserId` FOREIGN KEY (userId) REFERENCES Users(userId) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static final String createChatListTable = "create table if not exists Chats (chatId INTEGER PRIMARY KEY NOT NULL, " + + "title VARCHAR(50) NOT NULL);"; + public static final String createChatsStatsTable = "create table if not exists ChatStats (id INTEGER PRIMARY KEY AUTO_INCREMENT," + + " userId INTEGER NOT NULL, chatId INTEGER NOT NULL, date TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," + + " forwarded BOOLEAN NOT NULL DEFAULT false, type INTEGER NOT NULL," + + "CONSTRAINT `chatsStatsUserId` FOREIGN KEY (userId) REFERENCES Users(userId) ON UPDATE CASCADE ON DELETE CASCADE, " + + "CONSTRAINT `chatsStatsChatId` FOREIGN KEY (chatId) REFERENCES Chats(chatId) ON UPDATE CASCADE ON DELETE CASCADE);"; + public static volatile List operatingSystems = new ArrayList<>(); + + static { + operatingSystems.add("All"); + operatingSystems.add("iOS"); + operatingSystems.add("Webogram"); + operatingSystems.add("Android"); + operatingSystems.add("OSX"); + operatingSystems.add("Desktop"); + operatingSystems.add("WP"); + }*/ +} diff --git a/src/main/java/org/telegram/database/DatabaseManager.java b/src/main/java/org/telegram/database/DatabaseManager.java new file mode 100644 index 0000000..28a175b --- /dev/null +++ b/src/main/java/org/telegram/database/DatabaseManager.java @@ -0,0 +1,320 @@ +/* + * This is the source code of Telegram Bot v. 2.0 + * It is licensed under GNU GPL v. 3 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Ruben Bermudez, 3/12/14. + */ +package org.telegram.database; + +import org.telegram.services.BotLogger; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.HashMap; + +/** + * @author Ruben Bermudez + * @version 2.0 + * @brief Database Manager to perform database operations + * @date 3/12/14 + */ +public class DatabaseManager { + private static volatile BotLogger log = BotLogger.getLogger(DatabaseManager.class.getName()); ///< Logger + private static volatile DatabaseManager instance; + private static volatile ConectionDB connetion; + + /** + * Private constructor (due to Singleton) + */ + private DatabaseManager() { + connetion = new ConectionDB(); + final int currentVersion = connetion.checkVersion(); + log.info("Current db version: " + currentVersion); + if (currentVersion < CreationStrings.version) { + recreateTable(currentVersion); + } + } + + /** + * Get Singleton instance + * + * @return instance of the class + */ + public static DatabaseManager getInstance() { + final DatabaseManager currentInstance; + if (instance == null) { + synchronized (DatabaseManager.class) { + if (instance == null) { + instance = new DatabaseManager(); + } + currentInstance = instance; + } + } else { + currentInstance = instance; + } + return currentInstance; + } + + /** + * Recreates the DB + */ + private void recreateTable(int currentVersion) { + try { + connetion.initTransaction(); + if (currentVersion == 0) { + currentVersion = createNewTables(); + } + if (currentVersion == 1) { + currentVersion = updateToVersion2(); + } + if (currentVersion == 2) { + currentVersion = updateToVersion3(); + } + connetion.commitTransaction(); + } catch (SQLException e) { + log.error(e); + } + } + + private int updateToVersion2() throws SQLException { + connetion.executeQuery(CreationStrings.createRecentWeatherTable); + connetion.executeQuery("INSERT IGNORE INTO Versions (Version) VALUES(2);"); + return 2; + } + + private int updateToVersion3() throws SQLException { + connetion.executeQuery(CreationStrings.createDirectionsDatabase); + connetion.executeQuery("INSERT IGNORE INTO Versions (Version) VALUES(3);"); + return 2; + } + + private int createNewTables() throws SQLException { + connetion.executeQuery(CreationStrings.createVersionTable); + connetion.executeQuery(CreationStrings.createFilesTable); + connetion.executeQuery(CreationStrings.insertCurrentVersion); + connetion.executeQuery(CreationStrings.createUsersForFilesTable); + connetion.executeQuery(CreationStrings.createRecentWeatherTable); + connetion.executeQuery(CreationStrings.createDirectionsDatabase); + return CreationStrings.version; + } + + public boolean addFile(String fileId, Integer userId, String caption) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO Files (fileId, userId, caption) VALUES(?, ?, ?)"); + preparedStatement.setString(1, fileId); + preparedStatement.setInt(2, userId); + preparedStatement.setString(3, caption); + + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } + + public HashMap getFilesByUser(Integer userId) { + HashMap files = new HashMap<>(); + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT * FROM Files WHERE userId = ?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + while (result.next()) { + files.put(result.getString("fileId"), result.getString("caption")); + } + result.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return files; + } + + public boolean addUserForFile(Integer userId, int status) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO FilesUsers (userId, status) VALUES(?, ?)"); + preparedStatement.setInt(1, userId); + preparedStatement.setInt(2, status); + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } + + public boolean deleteUserForFile(Integer userId) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM FilesUsers WHERE userId=?;"); + preparedStatement.setInt(1, userId); + + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } + + public int getUserStatusForFile(Integer userId) { + int status = -1; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("Select status FROM FilesUsers WHERE userId=?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + if (result.next()) { + status = result.getInt("status"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return status; + } + + public boolean doesFileExists(String fileId) { + boolean exists = false; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("Select fileID FROM Files WHERE fileId=?"); + preparedStatement.setString(1, fileId); + final ResultSet result = preparedStatement.executeQuery(); + exists = result.next(); + } catch (SQLException e) { + e.printStackTrace(); + } + return exists; + } + + public boolean deleteFile(String fileId) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM Files WHERE fileId=?;"); + preparedStatement.setString(1, fileId); + + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } + + public boolean addRecentWeather(Integer userId, Integer cityId, String cityName) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO RecentWeather (userId, cityId, cityName) VALUES(?, ?, ?)"); + preparedStatement.setInt(1, userId); + preparedStatement.setInt(2, cityId); + preparedStatement.setString(3, cityName); + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + cleanUpRecent(userId); + return updatedRows > 0; + } + + public HashMap getRecentWeather(Integer userId) { + HashMap recentWeather = new HashMap<>(); + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("select * FROM RecentWeather WHERE userId=?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + while (result.next()) { + recentWeather.put(result.getInt("cityId"), result.getString("cityName")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + + return recentWeather; + } + + private void cleanUpRecent(Integer userId) { + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM RecentWeather WHERE userid = ? AND ID <= (SELECT ID FROM (SELECT id From RecentWeather where userId = ? ORDER BY id DESC LIMIT 1 OFFSET 4 ) AS T1 )"); + preparedStatement.setInt(1, userId); + preparedStatement.setInt(2, userId); + preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean addUserForDirection(Integer userId, int status, int messageId, String origin) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO Directions (userId, status, messageId, origin) VALUES(?, ?, ?, ?)"); + preparedStatement.setInt(1, userId); + preparedStatement.setInt(2, status); + preparedStatement.setInt(3, messageId); + if (origin == null || origin.isEmpty()) { + preparedStatement.setNull(4, Types.VARCHAR); + } else { + preparedStatement.setString(4, origin); + } + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } + + public int getUserDestinationStatus(Integer userId) { + int status = -1; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT status FROM Directions WHERE userId = ?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + if (result.next()) { + status = result.getInt("status"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return status; + } + + public int getUserDestinationMessageId(Integer userId) { + int messageId = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT messageId FROM Directions WHERE userId = ?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + if (result.next()) { + messageId = result.getInt("messageId"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return messageId; + } + + public String getUserOrigin(Integer userId) { + String origin = ""; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT origin FROM Directions WHERE userId = ?"); + preparedStatement.setInt(1, userId); + final ResultSet result = preparedStatement.executeQuery(); + if (result.next()) { + origin = result.getString("origin"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return origin; + } + + public boolean deleteUserForDirections(Integer userId) { + int updatedRows = 0; + try { + final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM Directions WHERE userId=?;"); + preparedStatement.setInt(1, userId); + + updatedRows = preparedStatement.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + return updatedRows > 0; + } +} diff --git a/src/main/java/org/telegram/methods/Constants.java b/src/main/java/org/telegram/methods/Constants.java new file mode 100644 index 0000000..0c39bb6 --- /dev/null +++ b/src/main/java/org/telegram/methods/Constants.java @@ -0,0 +1,11 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief TODO + * @date 20 of June of 2015 + */ +public class Constants { + public static final String BASEURL = "https://api.telegram.org/bot"; +} diff --git a/src/main/java/org/telegram/methods/ForwardMessage.java b/src/main/java/org/telegram/methods/ForwardMessage.java new file mode 100644 index 0000000..0599155 --- /dev/null +++ b/src/main/java/org/telegram/methods/ForwardMessage.java @@ -0,0 +1,46 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send text messages. On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class ForwardMessage { + public static final String PATH = "forwardmessage"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String FROMCHATID_FIELD = "from_chat_id"; + private Integer fromChatId; ///< Unique identifier for the chat where the original message was sent — User or GroupChat id + public static final String MESSAGEID_FIELD = "message_id"; + private Integer messageId; ///< Unique message identifier + + public ForwardMessage() { + super(); + } + + public Integer getChatId() { + return chatId; + } + + public void setChatId(Integer chatId) { + this.chatId = chatId; + } + + public Integer getFromChatId() { + return fromChatId; + } + + public void setFromChatId(Integer fromChatId) { + this.fromChatId = fromChatId; + } + + public Integer getMessageId() { + return messageId; + } + + public void setMessageId(Integer messageId) { + this.messageId = messageId; + } +} diff --git a/src/main/java/org/telegram/methods/GetMe.java b/src/main/java/org/telegram/methods/GetMe.java new file mode 100644 index 0000000..42019a5 --- /dev/null +++ b/src/main/java/org/telegram/methods/GetMe.java @@ -0,0 +1,12 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief A simple method for testing your bot's auth token. Requires no parameters. + * Returns basic information about the bot in form of a User object + * @date 20 of June of 2015 + */ +public class GetMe { + public static final String PATH = "getme"; +} diff --git a/src/main/java/org/telegram/methods/GetUpdates.java b/src/main/java/org/telegram/methods/GetUpdates.java new file mode 100644 index 0000000..2291fe8 --- /dev/null +++ b/src/main/java/org/telegram/methods/GetUpdates.java @@ -0,0 +1,63 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to receive incoming updates using long polling (wiki). + * An Array of Update objects is returned. + * @date 20 of June of 2015 + */ +public class GetUpdates { + public static final String PATH = "getupdates"; + + public static final String OFFSET_FIELD = "offset"; + /** + * Optional Identifier of the first update to be returned. + * Must be greater by one than the highest among the identifiers of previously received updates. + * By default, updates starting with the earliest unconfirmed update are returned. + */ + private Integer offset; + public static final String LIMIT_FIELD = "limit"; + /** + * Optional Limits the number of updates to be retrieved. + * Values between 1—100 are accepted. Defaults to 100 + */ + private Integer limit; + public static final String TIMEOUT_FIELD = "timeout"; + /** + * Optional Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling + */ + private Integer timeout; + + public GetUpdates() { + super(); + } + + public Integer getOffset() { + return offset; + } + + public void setOffset(Integer offset) { + this.offset = offset; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } + + public Integer getTimeout() { + return timeout; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public String getUrlParams() { + return "?" + OFFSET_FIELD + "=" + offset ; + } +} diff --git a/src/main/java/org/telegram/methods/GetUserProfilePhotos.java b/src/main/java/org/telegram/methods/GetUserProfilePhotos.java new file mode 100644 index 0000000..97254c9 --- /dev/null +++ b/src/main/java/org/telegram/methods/GetUserProfilePhotos.java @@ -0,0 +1,57 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos object. + * @date 20 of June of 2015 + */ +public class GetUserProfilePhotos { + public static final String PATH = "getuserprofilephotos"; +/* +user_id Integer Yes +offset Integer +limit Integer Optional + */ + public static final String USERID_FIELD = "user_id"; + private Integer userId; ///< Unique identifier of the target user + public static final String OFFSET_FIELD = "offset"; + /** + * Sequential number of the first photo to be returned. By default, all photos are returned. + */ + private Integer offset; + public static final String LIMIT_FIELD = "limit"; + /** + * Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. + */ + private Integer limit; + + public GetUserProfilePhotos() { + super(); + } + + + public Integer getUserId() { + return userId; + } + + public void setUserId(Integer userId) { + this.userId = userId; + } + + public Integer getOffset() { + return offset; + } + + public void setOffset(Integer offset) { + this.offset = offset; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } +} diff --git a/src/main/java/org/telegram/methods/SendAudio.java b/src/main/java/org/telegram/methods/SendAudio.java new file mode 100644 index 0000000..1caa53e --- /dev/null +++ b/src/main/java/org/telegram/methods/SendAudio.java @@ -0,0 +1,29 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send audio files, + * if you want Telegram clients to display the file as a playable voice message. + * For this to work, your audio must be in an .ogg file encoded with OPUS + * (other formats may be sent as Document). On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendAudio { + public static final String PATH = "sendaudio"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String AUDIO_FIELD = "audio"; + private String audio; ///< Audio file to send. file_id as String to resend an audio that is already on the Telegram servers + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard + + public SendAudio() { + super(); + } +} diff --git a/src/main/java/org/telegram/methods/SendChatAction.java b/src/main/java/org/telegram/methods/SendChatAction.java new file mode 100644 index 0000000..f68e668 --- /dev/null +++ b/src/main/java/org/telegram/methods/SendChatAction.java @@ -0,0 +1,31 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method when you need to tell the user that something is happening on the bot's side. + * The status is set for 5 seconds or less (when a message arrives from your bot, + * Telegram clients clear its typing status). + * @date 20 of June of 2015 + */ +public class SendChatAction { + + public static final String PATH = "sendChatAction"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String ACTION_FIELD = "action"; + /** + * Type of action to broadcast. + * Choose one, depending on what the user is about to receive: + * 'typing' for text messages + * 'upload_photo' for photos + * 'record_video' or 'upload_video' for videos + * 'record_audio' or 'upload_audio' for audio files + * 'upload_document' for general files, + * 'find_location' for location data. + */ + private Float action; +} diff --git a/src/main/java/org/telegram/methods/SendDocument.java b/src/main/java/org/telegram/methods/SendDocument.java new file mode 100644 index 0000000..757c7c0 --- /dev/null +++ b/src/main/java/org/telegram/methods/SendDocument.java @@ -0,0 +1,76 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send general files. On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendDocument { + public static final String PATH = "senddocument"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String DOCUMENT_FIELD = "document"; + private String document; ///< File file to send. file_id as String to resend a file that is already on the Telegram servers + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard + + private boolean isNewDocument; + private String documentName; + + public SendDocument() { + super(); + } + + public Integer getChatId() { + return chatId; + } + + public void setChatId(Integer chatId) { + this.chatId = chatId; + } + + public String getDocument() { + return document; + } + + public void setDocument(String document) { + this.document = document; + this.isNewDocument = false; + } + + public void setNewDocument(String document, String documentName) { + this.document = document; + this.isNewDocument = true; + this.documentName = documentName; + } + + public boolean isNewDocument() { + return isNewDocument; + } + + public String getDocumentName() { + return documentName; + } + + public Integer getReplayToMessageId() { + return replayToMessageId; + } + + public void setReplayToMessageId(Integer replayToMessageId) { + this.replayToMessageId = replayToMessageId; + } + + public ReplyKeyboard getReplayMarkup() { + return replayMarkup; + } + + public void setReplayMarkup(ReplyKeyboard replayMarkup) { + this.replayMarkup = replayMarkup; + } +} diff --git a/src/main/java/org/telegram/methods/SendLocation.java b/src/main/java/org/telegram/methods/SendLocation.java new file mode 100644 index 0000000..fb30147 --- /dev/null +++ b/src/main/java/org/telegram/methods/SendLocation.java @@ -0,0 +1,24 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send point on the map. On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendLocation { + public static final String PATH = "sendlocation"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String LATITUDE_FIELD = "latitude"; + private Float latitude; ///< Latitude of location + public static final String LONGITUDE_FIELD = "longitude"; + private Float longitude; ///< Longitude of location + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard +} diff --git a/src/main/java/org/telegram/methods/SendMessage.java b/src/main/java/org/telegram/methods/SendMessage.java new file mode 100644 index 0000000..ea1abd8 --- /dev/null +++ b/src/main/java/org/telegram/methods/SendMessage.java @@ -0,0 +1,68 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send text messages. On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendMessage { + public static final String PATH = "sendmessage"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String TEXT_FIELD = "text"; + private String text; ///< Text of the message to be sent + public static final String DISABLEWEBPAGEPREVIEW_FIELD = "disable_web_page_preview"; + private Boolean disableWebPagePreview; ///< Disables link previews for links in this message + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard + + public SendMessage() { + super(); + } + + public Integer getChatId() { + return chatId; + } + + public void setChatId(Integer chatId) { + this.chatId = chatId; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Integer getReplayToMessageId() { + return replayToMessageId; + } + + public void setReplayToMessageId(Integer replayToMessageId) { + this.replayToMessageId = replayToMessageId; + } + + public ReplyKeyboard getReplayMarkup() { + return replayMarkup; + } + + public void setReplayMarkup(ReplyKeyboard replayMarkup) { + this.replayMarkup = replayMarkup; + } + + public Boolean getDisableWebPagePreview() { + return disableWebPagePreview; + } + + public void setDisableWebPagePreview(Boolean disableWebPagePreview) { + this.disableWebPagePreview = disableWebPagePreview; + } +} diff --git a/src/main/java/org/telegram/methods/SendPhoto.java b/src/main/java/org/telegram/methods/SendPhoto.java new file mode 100644 index 0000000..a40f1ec --- /dev/null +++ b/src/main/java/org/telegram/methods/SendPhoto.java @@ -0,0 +1,29 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send photos. On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendPhoto { + public static final String PATH = "sendphoto"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String PHOTO_FIELD = "photo"; + private String photo; ///< Photo to send. file_id as String to resend a photo that is already on the Telegram servers + public static final String CAPTION_FIELD = "photo"; + private String caption; ///< Optional Photo caption (may also be used when resending photos by file_id). + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard + + + public SendPhoto() { + super(); + } +} diff --git a/src/main/java/org/telegram/methods/SendSticker.java b/src/main/java/org/telegram/methods/SendSticker.java new file mode 100644 index 0000000..cd94814 --- /dev/null +++ b/src/main/java/org/telegram/methods/SendSticker.java @@ -0,0 +1,22 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief TODO + * @date 20 of June of 2015 + */ +public class SendSticker { + public static final String PATH = "sendsticker"; + + public static final String CHATID_FIELD = "document"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String STICKER_FIELD = "sticker"; + private String sticker; ///< Sticker file to send. file_id as String to resend a sticker that is already on the Telegram servers + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard +} diff --git a/src/main/java/org/telegram/methods/SendVideo.java b/src/main/java/org/telegram/methods/SendVideo.java new file mode 100644 index 0000000..30c578d --- /dev/null +++ b/src/main/java/org/telegram/methods/SendVideo.java @@ -0,0 +1,24 @@ +package org.telegram.methods; + +import org.telegram.api.ReplyKeyboard; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to send video files, + * Telegram clients support mp4 videos (other formats may be sent as Document). + * On success, the sent Message is returned. + * @date 20 of June of 2015 + */ +public class SendVideo { + public static final String PATH = "sendvideo"; + + public static final String CHATID_FIELD = "chat_id"; + private Integer chatId; ///< Unique identifier for the message recepient — User or GroupChat id + public static final String VIDEO_FIELD = "video"; + private String video; ///< Video to send. file_id as String to resend a video that is already on the Telegram servers + public static final String REPLYTOMESSAGEID_FIELD = "reply_to_message_id"; + private Integer replayToMessageId; ///< Optional. If the message is a reply, ID of the original message + public static final String REPLYMARKUP_FIELD = "reply_markup"; + private ReplyKeyboard replayMarkup; ///< Optional. JSON-serialized object for a custom reply keyboard +} diff --git a/src/main/java/org/telegram/methods/SetWebhook.java b/src/main/java/org/telegram/methods/SetWebhook.java new file mode 100644 index 0000000..732a797 --- /dev/null +++ b/src/main/java/org/telegram/methods/SetWebhook.java @@ -0,0 +1,17 @@ +package org.telegram.methods; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Use this method to specify a url and receive incoming updates via an outgoing webhook. + * Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, + * containing a JSON-serialized Update. In case of an unsuccessful request, + * we will give up after a reasonable amount of attempts. + * @date 20 of June of 2015 + */ +public class SetWebhook { + public static final String PATH = "setwebhook"; + + public static final String URL_FIELD = "url"; + private String url; ///< HTTPS url to send updates to. Use an empty string to remove webhook integration +} diff --git a/src/main/java/org/telegram/services/BotLogger.java b/src/main/java/org/telegram/services/BotLogger.java new file mode 100644 index 0000000..84e77ed --- /dev/null +++ b/src/main/java/org/telegram/services/BotLogger.java @@ -0,0 +1,337 @@ +package org.telegram.services; + +import org.telegram.BuildVars; + +import javax.validation.constraints.NotNull; +import java.io.*; +import java.time.LocalDateTime; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Ruben Bermudez + * @version 2.0 + * @brief Logger to file + * @date 21/01/15 + */ +public class BotLogger { + private static final Object lockToWrite = new Object(); + private static volatile PrintWriter logginFile; + private static volatile String currentFileName; + private static volatile ConcurrentHashMap instances = new ConcurrentHashMap<>(); + private final Logger logger; + private LocalDateTime lastFileDate; + + private BotLogger(String classname) { + this.logger = Logger.getLogger(classname); + this.logger.setLevel(Level.ALL); + this.lastFileDate = LocalDateTime.now(); + if (currentFileName == null || currentFileName.compareTo("") == 0) { + currentFileName = BuildVars.pathToLogs + dateFormatterForFileName(this.lastFileDate) + ".log"; + try { + File file = new File(currentFileName); + if (file.exists()) { + logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); + } else { + boolean created = file.createNewFile(); + if (created) { + logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); + } else { + throw new NullPointerException("File for loggin error"); + } + } + } catch (IOException ignored) { + } + + } + } + + public static BotLogger getLogger(@NotNull String className) { + BotLogger currentInstance; + if (instances.containsKey(className)) { + currentInstance = instances.get(className); + } else { + synchronized (BotLogger.class) { + if (instances.containsKey(className)) { + currentInstance = instances.get(className); + } else { + BotLogger instance = new BotLogger(className); + instances.put(className, instance); + currentInstance = instance; + } + } + } + + return currentInstance; + } + + public void log(@NotNull Level level, String msg) { + this.logger.log(level, msg); + logToFile(level, msg); + } + + + public void severe(String msg) { + this.logger.severe(msg); + logToFile(Level.SEVERE, msg); + } + + public void warn(String msg) { + warning(msg); + } + + public void debug(String msg) { + fine(msg); + } + + public void error(String msg) { + severe(msg); + } + + public void trace(String msg) { + finer(msg); + } + + public void warning(String msg) { + this.logger.warning(msg); + logToFile(Level.WARNING, msg); + } + + + public void info(String msg) { + this.logger.info(msg); + logToFile(Level.INFO, msg); + } + + + public void config(String msg) { + this.logger.config(msg); + logToFile(Level.CONFIG, msg); + } + + + public void fine(String msg) { + this.logger.fine(msg); + logToFile(Level.FINE, msg); + } + + + public void finer(String msg) { + this.logger.finer(msg); + logToFile(Level.FINER, msg); + } + + + public void finest(String msg) { + this.logger.finest(msg); + logToFile(Level.FINEST, msg); + } + + + public void log(@NotNull Level level, @NotNull Throwable throwable) { + this.logger.log(level, "Exception", throwable); + logToFile(level, throwable); + } + + public void log(@NotNull Level level, String msg, Throwable thrown) { + this.logger.log(level, msg, thrown); + logToFile(level, msg, thrown); + } + + public void severe(@NotNull Throwable throwable) { + logToFile(Level.SEVERE, throwable); + } + + public void warning(@NotNull Throwable throwable) { + logToFile(Level.WARNING, throwable); + } + + public void info(@NotNull Throwable throwable) { + logToFile(Level.INFO, throwable); + } + + public void config(@NotNull Throwable throwable) { + logToFile(Level.CONFIG, throwable); + } + + public void fine(@NotNull Throwable throwable) { + logToFile(Level.FINE, throwable); + } + + public void finer(@NotNull Throwable throwable) { + logToFile(Level.FINER, throwable); + } + + public void finest(@NotNull Throwable throwable) { + logToFile(Level.FINEST, throwable); + } + + public void warn(Throwable throwable) { + warning(throwable); + } + + public void debug(Throwable throwable) { + fine(throwable); + } + + public void error(Throwable throwable) { + severe(throwable); + } + + public void trace(Throwable throwable) { + finer(throwable); + } + + public void severe(String msg, @NotNull Throwable throwable) { + log(Level.SEVERE, msg, throwable); + } + + public void warning(String msg, @NotNull Throwable throwable) { + log(Level.WARNING, msg, throwable); + } + + public void info(String msg, @NotNull Throwable throwable) { + log(Level.INFO, msg, throwable); + } + + public void config(String msg, @NotNull Throwable throwable) { + log(Level.CONFIG, msg, throwable); + } + + public void fine(String msg, @NotNull Throwable throwable) { + log(Level.FINE, msg, throwable); + } + + public void finer(String msg, @NotNull Throwable throwable) { + log(Level.FINER, msg, throwable); + } + + public void finest(String msg, @NotNull Throwable throwable) { + log(Level.FINEST, msg, throwable); + } + + public void warn(String msg, @NotNull Throwable throwable) { + log(Level.WARNING, msg, throwable); + } + + public void debug(String msg, @NotNull Throwable throwable) { + log(Level.FINE, msg, throwable); + } + + public void error(String msg, @NotNull Throwable throwable) { + log(Level.SEVERE, msg, throwable); + } + + public void trace(String msg, @NotNull Throwable throwable) { + log(Level.FINER, msg, throwable); + } + + private boolean isCurrentDate(LocalDateTime dateTime) { + return dateTime.toLocalDate().isEqual(this.lastFileDate.toLocalDate()); + } + + private String dateFormatterForFileName(@NotNull LocalDateTime dateTime) { + String dateString = ""; + dateString += dateTime.getDayOfMonth(); + dateString += dateTime.getMonthValue(); + dateString += dateTime.getYear(); + return dateString; + } + + private String dateFormatterForLogs(@NotNull LocalDateTime dateTime) { + String dateString = "["; + dateString += dateTime.getDayOfMonth() + "_"; + dateString += dateTime.getMonthValue() + "_"; + dateString += dateTime.getYear() + "_"; + dateString += dateTime.getHour() + ":"; + dateString += dateTime.getMinute() + ":"; + dateString += dateTime.getSecond(); + dateString += "] "; + return dateString; + } + + private void updateAndCreateFile(LocalDateTime dateTime) { + if (!isCurrentDate(dateTime)) { + this.lastFileDate = LocalDateTime.now(); + currentFileName = BuildVars.pathToLogs + dateFormatterForFileName(this.lastFileDate) + ".log"; + try { + logginFile.flush(); + logginFile.close(); + File file = new File(currentFileName); + if (file.exists()) { + logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); + } else { + boolean created = file.createNewFile(); + if (created) { + logginFile = new PrintWriter(new BufferedWriter(new FileWriter(currentFileName, true))); + } else { + throw new NullPointerException("Error updating log file"); + } + } + } catch (IOException ignored) { + } + } + } + + private void logToFile(@NotNull Level level, Throwable throwable) { + if (isLoggable(level)) { + synchronized (lockToWrite) { + LocalDateTime currentDate = LocalDateTime.now(); + String dateForLog = dateFormatterForLogs(currentDate); + updateAndCreateFile(currentDate); + logThrowableToFile(level, throwable, dateForLog); + } + } + } + + + private void logToFile(@NotNull Level level, String msg) { + if (isLoggable(level)) { + synchronized (lockToWrite) { + LocalDateTime currentDate = LocalDateTime.now(); + updateAndCreateFile(currentDate); + String dateForLog = dateFormatterForLogs(currentDate); + logMsgToFile(level, msg, dateForLog); + } + } + } + + private void logToFile(Level level, String msg, Throwable throwable) { + if (!isLoggable(level)) { + return; + } + synchronized (lockToWrite) { + LocalDateTime currentDate = LocalDateTime.now(); + updateAndCreateFile(currentDate); + String dateForLog = dateFormatterForLogs(currentDate); + logMsgToFile(level, msg, dateForLog); + logThrowableToFile(level, throwable, dateForLog); + } + } + + private void logMsgToFile(Level level, String msg, String dateForLog) { + dateForLog += level.toString() + " - " + msg; + logginFile.println(dateForLog); + logginFile.flush(); + } + + private void logThrowableToFile(Level level, Throwable throwable, String dateForLog) { + logginFile.println(dateForLog + level.getName() + " - " + throwable); + for (StackTraceElement element : throwable.getStackTrace()) { + logginFile.println("\tat " + element); + } + logginFile.flush(); + } + + private boolean isLoggable(Level level) { + return this.logger.isLoggable(level) && BuildVars.debug; + } + + @Override + protected void finalize() throws Throwable { + logginFile.flush(); + logginFile.close(); + super.finalize(); + } +} diff --git a/src/main/java/org/telegram/services/DirectionsService.java b/src/main/java/org/telegram/services/DirectionsService.java new file mode 100644 index 0000000..6b9219f --- /dev/null +++ b/src/main/java/org/telegram/services/DirectionsService.java @@ -0,0 +1,125 @@ +package org.telegram.services; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.json.JSONArray; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.telegram.BuildVars; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.format.DateTimeFormatter; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Weather service + * @date 20 of June of 2015 + */ +public class DirectionsService { + private static final String BASEURL = "https://maps.googleapis.com/maps/api/directions/json"; ///< Base url for REST + private static final String APIIDEND = "&key=" + BuildVars.DirectionsApiKey; + private static final String PARAMS = "&language=en&units=metric"; + private static final DateTimeFormatter dateFormaterFromDate = DateTimeFormatter.ofPattern("dd/MM/yyyy"); ///< Date to text formater + private static volatile DirectionsService instance; ///< Instance of this class + + /** + * Constructor (private due to singleton pattern) + */ + private DirectionsService() { + } + + /** + * Singleton + * + * @return Return the instance of this class + */ + public static DirectionsService getInstance() { + DirectionsService currentInstance; + if (instance == null) { + synchronized (DirectionsService.class) { + if (instance == null) { + instance = new DirectionsService(); + } + currentInstance = instance; + } + } else { + currentInstance = instance; + } + return currentInstance; + } + + /** + * Fetch the directions + * + * @param origin Origin address + * @param destination Destination address + * @return Destinations + */ + public String getDirections(String origin, String destination) { + String responseToUser; + try { + String completURL = BASEURL + "?origin=" + getQuery(origin) + "&destination=" + getQuery(destination) + PARAMS + APIIDEND; + HttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(completURL); + HttpResponse response = client.execute(request); + HttpEntity ht = response.getEntity(); + + BufferedHttpEntity buf = new BufferedHttpEntity(ht); + String responseContent = EntityUtils.toString(buf, "UTF-8"); + + JSONObject jsonObject = new JSONObject(responseContent); + if (jsonObject.getString("status").equals("OK")) { + JSONObject route = jsonObject.getJSONArray("routes").getJSONObject(0); + responseToUser = route.getJSONArray("legs").getJSONObject(0).getString("start_address"); + responseToUser += " is "; + responseToUser += route.getJSONArray("legs").getJSONObject(0).getJSONObject("distance").getString("text"); + responseToUser += " away from "; + responseToUser += route.getJSONArray("legs").getJSONObject(0).getString("end_address"); // TODO Destination + responseToUser += " and it takes "; + responseToUser += route.getJSONArray("legs").getJSONObject(0).getJSONObject("duration").getString("text"); + responseToUser += " to arrive there following these directions:\n\n"; + responseToUser += getDirectionsSteps(route.getJSONArray("legs").getJSONObject(0).getJSONArray("steps")); + } else { + responseToUser = "Directions not found between " + origin + " and " + destination; + } + } catch (IOException e) { + responseToUser = "Error fetching weather info"; + } + return responseToUser; + } + + private String getQuery(String address) throws UnsupportedEncodingException { + return URLEncoder.encode(address, "UTF-8"); + } + + public String getDirectionsSteps(JSONArray steps) { + String stepsStringify = ""; + for (int i = 0; i < steps.length(); i++) { + stepsStringify += i + ".\t" + getDirectionForStep(steps.getJSONObject(i)) + "\n\n"; + } + return stepsStringify; + } + + private String getDirectionForStep(JSONObject jsonObject) { + String direction = ""; + String htmlIntructions = Jsoup.parse(jsonObject.getString("html_instructions")).text(); + String duration = jsonObject.getJSONObject("duration").getString("text"); + String distance = jsonObject.getJSONObject("distance").getString("text"); + + direction += htmlIntructions + " "; + direction += " during "; + direction += duration; + direction += " (" + distance + ")"; + + return direction; + } +} diff --git a/src/main/java/org/telegram/services/TransifexService.java b/src/main/java/org/telegram/services/TransifexService.java new file mode 100644 index 0000000..1b58f1a --- /dev/null +++ b/src/main/java/org/telegram/services/TransifexService.java @@ -0,0 +1,355 @@ +package org.telegram.services; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.telegram.BuildVars; +import org.telegram.methods.SendDocument; + +import java.io.*; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Service that allow transifex files download + * @date 21 of June of 2015 + */ +public class TransifexService { + private static final String BASEURLAndroid = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/telegram/resource/stringsxml-48/translation/@language?file"; ///< Base url for REST + private static final String BASEURLiOS = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/iphone-1/resource/localizablestrings/translation/@language?file"; ///< Base url for REST + private static final String BASEURLOSX = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/osx/resource/localizablestrings/translation/@language?file"; ///< Base url for REST + private static final String BASEURLTDesktop = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/telegram-desktop/resource/langstrings/translation/@language?file"; ///< Base url for REST + private static final String BASEURLTemplates = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/telegram-desktop/resource/tl_generaltxt/translation/@language?file"; ///< Base url for REST + private static final String BASEURLWebogram = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/telegram-web/resource/en-usjson/translation/@language?file"; ///< Base url for REST + private static final String BASEURLWP = "http://" + BuildVars.TRANSIFEXUSER + ":" + BuildVars.TRANSIFEXPASSWORD + "@www.transifex.com/api/2/project/wp-telegram-messenger-beta/resource/appresourcesresx/translation/@language?file"; ///< Base url for REST + private static final int STATUS200 = 200; + private static final int BYTES1024 = 1024; + private static volatile TransifexService instance; ///< Instance of this class + + + /** + * Constructor (private due to singleton pattern) + */ + private TransifexService() { + } + + /** + * Singleton + * + * @return Return the instance of this class + */ + public static TransifexService getInstance() { + TransifexService currentInstance; + if (instance == null) { + synchronized (TransifexService.class) { + if (instance == null) { + instance = new TransifexService(); + } + currentInstance = instance; + } + } else { + currentInstance = instance; + } + return currentInstance; + } + + private String getFileAndroid(String query) { + String result = null; + try { + CloseableHttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(BASEURLAndroid.replace("@language", query)); + HttpResponse response = client.execute(request); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); + String line; + String responseString = ""; + while ((line = rd.readLine()) != null) { + responseString += line; + } + + if (response.getStatusLine().getStatusCode() == STATUS200) { + result = responseString; + } + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private byte[] getFileiOS(String query) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLiOS.replace("@language", query)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-16LE")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private byte[] getFileOSX(String query) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLOSX.replace("@language", query)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-16LE")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private byte[] getFileTDesktop(String query) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLTDesktop.replace("@language", query)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-16LE")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + public byte[] getFileTemplate(String languageCode) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLTemplates.replace("@language", languageCode)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private byte[] getFileWebogram(String query) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLWebogram.replace("@language", query)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-16LE")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + private byte[] getFileWP(String query) { + byte[] result = null; + try { + HttpClient client = HttpClientBuilder.create().build(); + HttpGet request = new HttpGet(BASEURLWP.replace("@language", query)); + HttpResponse response = client.execute(request); + result = IOUtils.toByteArray(new InputStreamReader(response.getEntity().getContent(), "UTF-16LE")); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + /** + * For languages that are composited of a regional part, change that part to uper case for transifex + * @param language Language received + * @return Language fixed + */ + private String fixCaseCompositedLanguages(String language) { + String[] parts = language.split("_"); + if (parts.length == 1) { + language = parts[0]; + } else { + language = parts[0] + "_" + parts[1].toUpperCase(); + } + return language; + } + + /** + * Fetch the language file for support members of android + * @param language Language requested + */ + public SendDocument getAndroidSupportLanguageFile(String language) { + String file = getFileAndroid(language); + SendDocument sendDocument; + if (file != null && file.getBytes().length / BYTES1024 >= 10) { + file = file.replaceAll("\"LanguageName\"\\>(\\w*)\\<\\/string\\>", "\"LanguageName\"\\>$1_1\\<\\/string\\>").replaceAll("\"LanguageCode\"\\>(\\w*)\\<\\/string\\>", "\"LanguageCode\"\\>$1_1\\<\\/string\\>"); + try { + String fileName = "languages_Android_" + language + ".xml"; + PrintWriter localFile = new PrintWriter(fileName); + localFile.print(file); + localFile.close(); + File fileToUpload = new File(fileName); + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } catch (FileNotFoundException e) { + e.printStackTrace(); + sendDocument = null; + } + } else { + sendDocument = null; + } + return sendDocument; + } + + /** + * Fetch the language file for Android + * @param language Language requested + */ + public SendDocument getAndroidLanguageFile(String language) { + String file = getFileAndroid(language); + SendDocument sendDocument; + if (file != null && file.getBytes().length / BYTES1024 >= 10) { + try { + String fileName = "languages_Android_" + language + ".xml"; + PrintWriter localFile = new PrintWriter(fileName); + localFile.print(file); + localFile.close(); + File fileToUpload = new File(fileName); + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } catch (FileNotFoundException e) { + e.printStackTrace(); + sendDocument = null; + } + } else { + sendDocument = null; + } + return sendDocument; + } + + /** + * Fetch the language file for iOS + * + * @param language Language requested + */ + public SendDocument getiOSLanguageFile(String language) { + byte[] file = getFileiOS(language); + SendDocument sendDocument; + if (file != null && file.length / BYTES1024 >= 10) { + try { + String fileName = "languages_ios_" + language + ".strings"; + File fileToUpload = new File(fileName); + FileOutputStream output = new FileOutputStream(fileToUpload); + IOUtils.write(file, output); + output.close(); + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } catch (IOException e) { + e.printStackTrace(); + sendDocument = null; + } + } else { + sendDocument = null; + } + return sendDocument; + } + + /** + * Fetch the language file for OSX + * @param language Language requested + */ + public SendDocument getOSXLanguageFile(String language) { + byte[] file = getFileOSX(language); + SendDocument sendDocument; + if (file != null && file.length / BYTES1024 >= 10) { + try { + String fileName = "languages_osx_" + language + ".strings"; + File fileToUpload = new File(fileName); + FileOutputStream output = new FileOutputStream(fileToUpload); + IOUtils.write(file, output); + output.close(); + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } catch (IOException e) { + e.printStackTrace(); + sendDocument = null; + } + } else { + sendDocument = null; + } + return sendDocument; + } + + /** + * Fetch the language file for Tdesktop + * @param language Language requested + */ + public SendDocument getTdesktopLanguageFile(String language) { + byte[] file = getFileTDesktop(language); + SendDocument sendDocument = null; + if (file != null && file.length / BYTES1024 >= 10) { + try { + String fileName = "languages_tdesktop_" + language + ".strings"; + File fileToUpload = new File(fileName); + FileOutputStream output = new FileOutputStream(fileToUpload); + IOUtils.write(file, output); + output.close(); + if (fileToUpload.exists()) { + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return sendDocument; + } + + /** + * Fetch the language file for Webogram + * @param language Language requested + */ + public SendDocument getWebogramLanguageFile(String language) { + byte[] file = getFileWebogram(language); + SendDocument sendDocument = null; + if (file != null && file.length / BYTES1024 >= 10) { + try { + String fileName = "languages_webogram_" + language + ".json"; + File fileToUpload = new File(fileName); + FileOutputStream output = new FileOutputStream(fileToUpload); + IOUtils.write(file, output); + output.close(); + if (fileToUpload.exists()) { + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return sendDocument; + } + + /** + * Fetch the language file for WP + * @param language Language requested + */ + public SendDocument getWPLanguageFile(String language) { + byte[] file = getFileWP(language); + SendDocument sendDocument = null; + if (file != null && file.length / BYTES1024 >= 10) { + try { + String fileName = "languages_wp_" + language + ".xml"; + File fileToUpload = new File(fileName); + FileOutputStream output = new FileOutputStream(fileToUpload); + IOUtils.write(file, output); + output.close(); + if (fileToUpload.exists()) { + sendDocument = new SendDocument(); + sendDocument.setNewDocument(fileToUpload.getAbsolutePath(), fileName); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return sendDocument; + } +} diff --git a/src/main/java/org/telegram/services/WeatherService.java b/src/main/java/org/telegram/services/WeatherService.java new file mode 100644 index 0000000..3d8c2b9 --- /dev/null +++ b/src/main/java/org/telegram/services/WeatherService.java @@ -0,0 +1,289 @@ +package org.telegram.services; + +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.json.JSONObject; +import org.telegram.BuildVars; +import org.telegram.database.DatabaseManager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Weather service + * @date 20 of June of 2015 + */ +public class WeatherService { + private static final String BASEURL = "http://api.openweathermap.org/data/2.5/"; ///< Base url for REST + private static final String FORECASTPATH = "forecast/daily"; + private static final String CURRENTPATH = "weather"; + private static final String APIIDEND = "&APPID=" + BuildVars.OPENWEATHERAPIKEY; + private static final String FORECASTPARAMS = "&cnt=3&units=metric"; + private static final String CURRENTPARAMS = "&cnt=1&units=metric"; + private static final DateTimeFormatter dateFormaterFromDate = DateTimeFormatter.ofPattern("dd/MM/yyyy"); ///< Date to text formater + private static volatile WeatherService instance; ///< Instance of this class + + /** + * Constructor (private due to singleton pattern) + */ + private WeatherService() { + } + + /** + * Singleton + * + * @return Return the instance of this class + */ + public static WeatherService getInstance() { + WeatherService currentInstance; + if (instance == null) { + synchronized (WeatherService.class) { + if (instance == null) { + instance = new WeatherService(); + } + currentInstance = instance; + } + } else { + currentInstance = instance; + } + return currentInstance; + } + + /** + * Fetch the weather of a city + * + * @param city City to get the weather + * @return userHash to be send to use + * @note Forecast for the following 3 days + */ + public String fetchWeatherForecast(String city, Integer userId) { + String cityFound; + String responseToUser; + try { + String completURL = BASEURL + FORECASTPATH + "?" + getCityQuery(city) + FORECASTPARAMS + APIIDEND; + HttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(completURL); + HttpResponse response = client.execute(request); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line; + String responseString = ""; + while ((line = rd.readLine()) != null) { + responseString += line; + } + + JSONObject jsonObject = new JSONObject(responseString); + if (jsonObject.getInt("cod") == 200) { + cityFound = jsonObject.getJSONObject("city").getString("name") + " (" + + jsonObject.getJSONObject("city").getString("country") + ")"; + saveRecentWeather(userId, cityFound, jsonObject.getJSONObject("city").getInt("id")); + responseToUser = "The weather for " + cityFound + " will be:\n\n"; + responseToUser += convertListOfForecastToString(jsonObject); + responseToUser += "Thank you for using our Weather Bot.\n\n" + + "Your Telegram Team"; + } else { + responseToUser = "City not found"; + } + } catch (IOException e) { + responseToUser = "Error fetching weather info"; + } + return responseToUser; + } + + /** + * Fetch the weather of a city + * + * @return userHash to be send to use + * @note Forecast for the following 3 days + */ + public String fetchWeatherForecastByLocation(Double longitude, Double latitude, Integer userId) { + String cityFound; + String responseToUser; + try { + String completURL = BASEURL + FORECASTPATH + "?lat=" + URLEncoder.encode(latitude + "", "UTF-8") + "&lon=" + URLEncoder.encode(longitude + "", "UTF-8") + FORECASTPARAMS + APIIDEND;; + HttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(completURL); + HttpResponse response = client.execute(request); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line; + String responseString = ""; + while ((line = rd.readLine()) != null) { + responseString += line; + } + + JSONObject jsonObject = new JSONObject(responseString); + if (jsonObject.getInt("cod") == 200) { + cityFound = jsonObject.getJSONObject("city").getString("name") + " (" + + jsonObject.getJSONObject("city").getString("country") + ")"; + saveRecentWeather(userId, cityFound, jsonObject.getJSONObject("city").getInt("id")); + responseToUser = "The weather for " + cityFound + " will be:\n\n"; + responseToUser += convertListOfForecastToString(jsonObject); + responseToUser += "Thank you for using our Weather Bot.\n\n" + + "Your Telegram Team"; + } else { + responseToUser = "City not found"; + } + } catch (IOException e) { + responseToUser = "Error fetching weather info"; + } + return responseToUser; + } + + /** + * Fetch the weather of a city + * + * @param city City to get the weather + * @return userHash to be send to use + * @note Forecast for the following 3 days + */ + public String fetchWeatherCurrent(String city, Integer userId) { + String cityFound; + String responseToUser; + try { + String completURL = BASEURL + CURRENTPATH + "?" + getCityQuery(city) + CURRENTPARAMS + APIIDEND; + HttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(completURL); + HttpResponse response = client.execute(request); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line; + String responseString = ""; + while ((line = rd.readLine()) != null) { + responseString += line; + } + + JSONObject jsonObject = new JSONObject(responseString); + if (jsonObject.getInt("cod") == 200) { + cityFound = jsonObject.getString("name") + " (" + + jsonObject.getJSONObject("sys").getString("country") + ")"; + saveRecentWeather(userId, cityFound, jsonObject.getInt("id")); + responseToUser = "The weather for " + cityFound + " is:\n\n"; + responseToUser += convertCurrentWeatherToString(jsonObject); + responseToUser += "Thank you for using our Weather Bot.\n\n" + + "Your Telegram Team"; + } else { + responseToUser = "City not found"; + } + } catch (IOException e) { + responseToUser = "Error fetching weather info"; + } + return responseToUser; + } + + /** + * Fetch the weather of a city + * + * @return userHash to be send to use + * @note Forecast for the following 3 days + */ + public String fetchWeatherCurrentByLocation(Double longitude, Double latitude, Integer userId) { + String cityFound; + String responseToUser; + try { + String completURL = BASEURL + CURRENTPATH + "?q=" + URLEncoder.encode("lat=" + latitude + "&lon=" + longitude, "UTF-8") + CURRENTPARAMS + APIIDEND;; + HttpClient client = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + HttpGet request = new HttpGet(completURL); + HttpResponse response = client.execute(request); + BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line; + String responseString = ""; + while ((line = rd.readLine()) != null) { + responseString += line; + } + + JSONObject jsonObject = new JSONObject(responseString); + if (jsonObject.getInt("cod") == 200) { + cityFound = jsonObject.getString("name") + " (" + + jsonObject.getJSONObject("sys").getString("country") + ")"; + saveRecentWeather(userId, cityFound, jsonObject.getInt("id")); + responseToUser = "The weather for " + cityFound + " is:\n\n"; + responseToUser += convertCurrentWeatherToString(jsonObject); + responseToUser += "Thank you for using our Weather Bot.\n\n" + + "Your Telegram Team"; + } else { + responseToUser = "City not found"; + } + } catch (IOException e) { + responseToUser = "Error fetching weather info"; + } + return responseToUser; + } + + private String convertCurrentWeatherToString(JSONObject jsonObject) { + String temp = jsonObject.getJSONObject("main").getDouble("temp")+""; + String cloudiness = jsonObject.getJSONObject("clouds").getInt("all") + "%"; + String weatherDesc = jsonObject.getJSONArray("weather").getJSONObject(0).getString("description"); + + String responseToUser = ""; + responseToUser += + " |-- Weather: " + weatherDesc + "\n" + + " |-- Cloudiness: " + cloudiness + "\n" + + " |-- Temperature: " + temp + "ºC\n\n"; + + return responseToUser; + } + + /** + * Convert a list of weather forcast to a list of strings to be sent + * + * @param jsonObject JSONObject contining the list + * @return String to be sent to the user + */ + private String convertListOfForecastToString(JSONObject jsonObject) { + String responseToUser = ""; + for (int i = 0; i < jsonObject.getJSONArray("list").length(); i++) { + JSONObject internalJSON = jsonObject.getJSONArray("list").getJSONObject(i); + responseToUser += convertInternalInformationToString(internalJSON); + } + return responseToUser; + } + + /** + * Convert internal part of then answer to string + * + * @param internalJSON JSONObject containing the part to convert + * @return String to be sent to the user + */ + private String convertInternalInformationToString(JSONObject internalJSON) { + String responseToUser = ""; + LocalDate date; + String tempMax; + String tempMin; + String weatherDesc; + date = Instant.ofEpochSecond(internalJSON.getLong("dt")).atZone(ZoneId.systemDefault()).toLocalDate(); + tempMax = internalJSON.getJSONObject("temp").getDouble("max") + ""; + tempMin = internalJSON.getJSONObject("temp").getDouble("min") + ""; + JSONObject weatherObject = internalJSON.getJSONArray("weather").getJSONObject(0); + weatherDesc = weatherObject.getString("description"); + + responseToUser += "*On " + dateFormaterFromDate.format(date) + "\n" + + " |--Forecast: " + weatherDesc + "\n" + + " |--High temperature: " + tempMax + "ºC\n" + + " |--Low temperature: " + tempMin + "ºC\n\n"; + return responseToUser; + } + + private void saveRecentWeather(Integer userId, String cityName, int cityId) { + DatabaseManager.getInstance().addRecentWeather(userId, cityId, cityName); + } + + private String getCityQuery(String city) throws UnsupportedEncodingException { + String cityQuery = ""; + try { + cityQuery += "id=" + URLEncoder.encode(Integer.parseInt(city)+"", "UTF-8"); + } catch(NumberFormatException | NullPointerException e) { + cityQuery += "q=" + URLEncoder.encode(city, "UTF-8"); + } + return cityQuery; + } +} diff --git a/src/main/java/org/telegram/updateshandlers/DirectionsHandlers.java b/src/main/java/org/telegram/updateshandlers/DirectionsHandlers.java new file mode 100644 index 0000000..6e79bd4 --- /dev/null +++ b/src/main/java/org/telegram/updateshandlers/DirectionsHandlers.java @@ -0,0 +1,126 @@ +package org.telegram.updateshandlers; + +import org.telegram.*; +import org.telegram.api.ForceReply; +import org.telegram.api.Message; +import org.telegram.api.ReplyKeyboardHide; +import org.telegram.api.Update; +import org.telegram.database.DatabaseManager; +import org.telegram.methods.SendMessage; +import org.telegram.services.DirectionsService; +import org.telegram.updatesreceivers.UpdatesThread; +import org.telegram.updatesreceivers.Webhook; + +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Handler for updates to Directions Bot + * @date 24 of June of 2015 + */ +public class DirectionsHandlers implements UpdatesCallback { + private static final String TOKEN = BotConfig.TOKENDIRECTIONS; + private final int webhookPort = 9993; + private final Webhook webhook; + private final UpdatesThread updatesThread; + private static final int WATING_ORIGIN_STATUS = 0; + private static final int WATING_DESTINY_STATUS = 1; + + public DirectionsHandlers() { + if (BuildVars.useWebHook) { + webhook = new Webhook(this, webhookPort); + updatesThread = null; + SenderHelper.SendWebhook(webhook.getURL(), TOKEN); + } else { + webhook = null; + SenderHelper.SendWebhook("", TOKEN); + updatesThread = new UpdatesThread(TOKEN, this); + } + } + + @Override + public void onUpdateReceived(Update update) { + handleDirections(update); + } + + @Override + public void onUpdatesReceived(List updates) { + for (Update update: updates) { + handleDirections(update); + } + } + + public void handleDirections(Update update) { + Message message = update.getMessage(); + if (message != null && message.hasText()) { + if (message.getText().startsWith(Commands.startDirectionCommand)) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setChatId(message.getChatId()); + sendMessageRequest.setReplayToMessageId(message.getMessageId()); + ForceReply forceReply = new ForceReply(); + forceReply.setSelective(true); + sendMessageRequest.setReplayMarkup(forceReply); + sendMessageRequest.setText(CustomMessages.initDirections); + Message sentMessage = SenderHelper.SendMessage(sendMessageRequest, TOKEN); + if (sentMessage != null) { + DatabaseManager.getInstance().addUserForDirection(message.getFrom().getId(), WATING_ORIGIN_STATUS, + sentMessage.getMessageId(),null); + } + } else if (message.getText().startsWith(Commands.help) || + (message.getText().startsWith(Commands.startCommand) || !message.isGroupMessage())) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpDirections); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else if (!message.getText().startsWith("/")){ + if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == WATING_ORIGIN_STATUS && + message.hasReplayMessage() && + DatabaseManager.getInstance().getUserDestinationMessageId(message.getFrom().getId()) == message.getReplyToMessage().getMessageId()) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setChatId(message.getChatId()); + sendMessageRequest.setReplayToMessageId(message.getMessageId()); + ForceReply forceReply = new ForceReply(); + forceReply.setSelective(true); + sendMessageRequest.setReplayMarkup(forceReply); + sendMessageRequest.setText(CustomMessages.sendDestination); + Message sentMessage = SenderHelper.SendMessage(sendMessageRequest, TOKEN); + if (sentMessage != null) { + DatabaseManager.getInstance().addUserForDirection(message.getFrom().getId(), WATING_DESTINY_STATUS, + sentMessage.getMessageId(), message.getText()); + } + + } else if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == WATING_DESTINY_STATUS && + message.hasReplayMessage() && + DatabaseManager.getInstance().getUserDestinationMessageId(message.getFrom().getId()) == message.getReplyToMessage().getMessageId()) { + String origin = DatabaseManager.getInstance().getUserOrigin(message.getFrom().getId()); + String destiny = message.getText(); + String directions = DirectionsService.getInstance().getDirections(origin, destiny); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setChatId(message.getChatId()); + ReplyKeyboardHide replyKeyboardHide = new ReplyKeyboardHide(); + replyKeyboardHide.setSelective(true); + sendMessageRequest.setReplayMarkup(replyKeyboardHide); + sendMessageRequest.setReplayToMessageId(message.getMessageId()); + sendMessageRequest.setText(directions); + Message sentMessage = SenderHelper.SendMessage(sendMessageRequest, TOKEN); + if (sentMessage != null) { + DatabaseManager.getInstance().deleteUserForDirections(message.getFrom().getId()); + } + } else if (!message.hasReplayMessage()) { + if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == -1) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpDirections); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.youNeedReplyDirections); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } + } + } + } +} diff --git a/src/main/java/org/telegram/updateshandlers/FilesHandlers.java b/src/main/java/org/telegram/updateshandlers/FilesHandlers.java new file mode 100644 index 0000000..3e2e170 --- /dev/null +++ b/src/main/java/org/telegram/updateshandlers/FilesHandlers.java @@ -0,0 +1,158 @@ +package org.telegram.updateshandlers; + +import org.telegram.*; +import org.telegram.api.Message; +import org.telegram.api.ReplyKeyboardMarkup; +import org.telegram.api.Update; +import org.telegram.database.DatabaseManager; +import org.telegram.methods.SendDocument; +import org.telegram.methods.SendMessage; +import org.telegram.updatesreceivers.UpdatesThread; +import org.telegram.updatesreceivers.Webhook; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Handler for updates to Files Bot + * @date 24 of June of 2015 + */ +public class FilesHandlers implements UpdatesCallback { + private static final String TOKEN = BotConfig.TOKENFILES; + private static final int webhookPort = 9992; + + private static final int INITIAL_UPLOAD_STATUS = 0; + private static final int DELETE_UPLOADED_STATUS = 1; + private final Webhook webhook; + private final UpdatesThread updatesThread; + + public FilesHandlers() { + if (BuildVars.useWebHook) { + webhook = new Webhook(this, webhookPort); + updatesThread = null; + SenderHelper.SendWebhook(webhook.getURL(), TOKEN); + } else { + webhook = null; + SenderHelper.SendWebhook("", TOKEN); + updatesThread = new UpdatesThread(TOKEN, this); + } + } + + @Override + public void onUpdateReceived(Update update) { + handleFileUpdate(update); + } + + @Override + public void onUpdatesReceived(List updates) { + for (Update update: updates) { + handleFileUpdate(update); + } + } + + public void handleFileUpdate(Update update) { + Message message = update.getMessage(); + if (message != null && message.hasText()) { + String[] parts = message.getText().split(" ", 2); + if (parts[0].startsWith(Commands.startCommand)) { + if (parts.length == 2) { + if (DatabaseManager.getInstance().doesFileExists(parts[1].trim())) { + SendDocument sendDocumentRequest = new SendDocument(); + sendDocumentRequest.setDocument(parts[1].trim()); + sendDocumentRequest.setChatId(message.getChatId()); + SenderHelper.SendDocument(sendDocumentRequest, TOKEN); + } else { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.wrongFileId); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } else { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpFiles); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } else if (!message.isGroupMessage()){ + if (parts[0].startsWith(Commands.uploadCommand)) { // Open upload for user + DatabaseManager.getInstance().addUserForFile(message.getFrom().getId(), INITIAL_UPLOAD_STATUS); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.sendFileToUpload); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else if (parts[0].startsWith(Commands.cancelCommand)) { + DatabaseManager.getInstance().deleteUserForFile(message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.processFinished); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else if (parts[0].startsWith(Commands.deleteCommand)) { + if (DatabaseManager.getInstance().getUserStatusForFile(message.getFrom().getId()) == DELETE_UPLOADED_STATUS && + parts.length == 2) { + String[] innerParts = parts[1].split("-->", 2); + boolean removed = DatabaseManager.getInstance().deleteFile(innerParts[0].trim()); + SendMessage sendMessageRequest = new SendMessage(); + if (removed) { + sendMessageRequest.setText(CustomMessages.fileDeleted); + } else { + sendMessageRequest.setText(CustomMessages.wrongFileId); + } + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + DatabaseManager.getInstance().deleteUserForFile(message.getFrom().getId()); + } else { + DatabaseManager.getInstance().addUserForFile(message.getFrom().getId(), DELETE_UPLOADED_STATUS); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.deleteUploadedFile); + sendMessageRequest.setChatId(message.getChatId()); + HashMap files = DatabaseManager.getInstance().getFilesByUser(message.getFrom().getId()); + ReplyKeyboardMarkup replyKeyboardMarkup = new ReplyKeyboardMarkup(); + if (files.size() > 0) { + List> commands = new ArrayList<>(); + for (Map.Entry entry : files.entrySet()) { + List commandRow = new ArrayList<>(); + commandRow.add(Commands.deleteCommand + " " + entry.getKey() + " --> " + entry.getValue()); + commands.add(commandRow); + } + replyKeyboardMarkup.setResizeKeyboard(true); + replyKeyboardMarkup.setOneTimeKeyboad(true); + replyKeyboardMarkup.setKeyboard(commands); + } + sendMessageRequest.setReplayMarkup(replyKeyboardMarkup); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } else if (parts[0].startsWith(Commands.listCommand)) { + HashMap files = DatabaseManager.getInstance().getFilesByUser(message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + if (files.size() > 0) { + String text = CustomMessages.listOfFiles + ":\n\n"; + for (Map.Entry entry : files.entrySet()) { + text += CustomMessages.uploadedFileURL + entry.getKey() + " --> " + entry.getValue() + "\n"; + } + sendMessageRequest.setText(text); + } else { + sendMessageRequest.setText(CustomMessages.noFiles); + } + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpFiles); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } + } else if ( message != null && message.hasDocument() + && DatabaseManager.getInstance().getUserStatusForFile(message.getFrom().getId()) == INITIAL_UPLOAD_STATUS) { + DatabaseManager.getInstance().addFile(message.getDocument().getFileId(), message.getFrom().getId(), message.getDocument().getFileName()); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.fileUploaded + CustomMessages.uploadedFileURL + message.getDocument().getFileId()); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } +} diff --git a/src/main/java/org/telegram/updateshandlers/TransifexHandlers.java b/src/main/java/org/telegram/updateshandlers/TransifexHandlers.java new file mode 100644 index 0000000..447215b --- /dev/null +++ b/src/main/java/org/telegram/updateshandlers/TransifexHandlers.java @@ -0,0 +1,91 @@ +package org.telegram.updateshandlers; + +import org.telegram.*; +import org.telegram.api.Message; +import org.telegram.api.Update; +import org.telegram.methods.SendDocument; +import org.telegram.methods.SendMessage; +import org.telegram.services.TransifexService; +import org.telegram.updatesreceivers.UpdatesThread; +import org.telegram.updatesreceivers.Webhook; + +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Handler for updates to Transifex Bot + * @date 24 of June of 2015 + */ +public class TransifexHandlers implements UpdatesCallback { + private static final String TOKEN = BotConfig.TOKENTRANSIFEX; + private static final int webhookPort = 9991; + private final Webhook webhook; + private final UpdatesThread updatesThread; + + public TransifexHandlers() { + if (BuildVars.useWebHook) { + webhook = new Webhook(this, webhookPort); + updatesThread = null; + SenderHelper.SendWebhook(webhook.getURL(), TOKEN); + } else { + webhook = null; + SenderHelper.SendWebhook("", TOKEN); + updatesThread = new UpdatesThread(TOKEN, this); + } + } + + @Override + public void onUpdateReceived(Update update) { + sendTransifexFile(update); + } + + @Override + public void onUpdatesReceived(List updates) { + for (Update update: updates) { + sendTransifexFile(update); + } + } + + public void sendTransifexFile(Update update) { + Message message = update.getMessage(); + if (message != null && message.hasText()) { + String text = message.getText(); + String[] parts = text.split(" ", 2); + SendDocument sendDocument = null; + if (parts.length == 2) { + if (parts[0].startsWith(Commands.transifexiOSCommand)) { + sendDocument = TransifexService.getInstance().getiOSLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexAndroidCommand)) { + sendDocument = TransifexService.getInstance().getAndroidLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexTDesktop)) { + sendDocument = TransifexService.getInstance().getTdesktopLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexWebogram)) { + sendDocument = TransifexService.getInstance().getWebogramLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexWP)) { + sendDocument = TransifexService.getInstance().getWPLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexOSX)) { + sendDocument = TransifexService.getInstance().getOSXLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.transifexAndroidSupportCommand)) { + sendDocument = TransifexService.getInstance().getAndroidSupportLanguageFile(parts[1].trim()); + } else if (parts[0].startsWith(Commands.help)) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpTransifex); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + + if (sendDocument != null) { + sendDocument.setChatId(message.getChatId()); + SenderHelper.SendDocument(sendDocument, TOKEN); + } + } else if (parts[0].startsWith(Commands.help) || + (message.getText().startsWith(Commands.startCommand) || !message.isGroupMessage())) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpTransifex); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } + } +} diff --git a/src/main/java/org/telegram/updateshandlers/UpdatesCallback.java b/src/main/java/org/telegram/updateshandlers/UpdatesCallback.java new file mode 100644 index 0000000..04798d7 --- /dev/null +++ b/src/main/java/org/telegram/updateshandlers/UpdatesCallback.java @@ -0,0 +1,16 @@ +package org.telegram.updateshandlers; + +import org.telegram.api.Update; + +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Callback to handle updates. Must support both, single update and List of updates + * @date 20 of June of 2015 + */ +public interface UpdatesCallback { + void onUpdateReceived(Update update); + void onUpdatesReceived(List updates); +} diff --git a/src/main/java/org/telegram/updateshandlers/WeatherHandlers.java b/src/main/java/org/telegram/updateshandlers/WeatherHandlers.java new file mode 100644 index 0000000..1be311f --- /dev/null +++ b/src/main/java/org/telegram/updateshandlers/WeatherHandlers.java @@ -0,0 +1,178 @@ +package org.telegram.updateshandlers; + +import org.telegram.*; +import org.telegram.api.ForceReply; +import org.telegram.api.Message; +import org.telegram.api.ReplyKeyboardMarkup; +import org.telegram.api.Update; +import org.telegram.database.DatabaseManager; +import org.telegram.methods.SendMessage; +import org.telegram.services.BotLogger; +import org.telegram.services.WeatherService; +import org.telegram.updatesreceivers.UpdatesThread; +import org.telegram.updatesreceivers.Webhook; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Handler for updates to Weather Bot + * @date 24 of June of 2015 + */ +public class WeatherHandlers implements UpdatesCallback { + private static volatile BotLogger log = BotLogger.getLogger(WeatherHandlers.class.getName()); + + private static final String TOKEN = BotConfig.TOKENWEATHER; + private static final int CURRENTWEATHERID = 0; + private static final int FORECASTWEATHERID = 1; + + private static final int webhookPort = 9990; + private final Webhook webhook; + private final UpdatesThread updatesThread; + private ConcurrentHashMap listOfSentMessages = new ConcurrentHashMap<>(); + + public WeatherHandlers() { + if (BuildVars.useWebHook) { + webhook = new Webhook(this, webhookPort); + updatesThread = null; + SenderHelper.SendWebhook(webhook.getURL(), TOKEN); + } else { + webhook = null; + SenderHelper.SendWebhook("", TOKEN); + updatesThread = new UpdatesThread(TOKEN, this); + } + } + + @Override + public void onUpdateReceived(Update update) { + sendWeatherInformation(update); + } + + @Override + public void onUpdatesReceived(List updates) { + for (Update update: updates) { + sendWeatherInformation(update); + } + } + + public void sendWeatherInformation(Update update) { + Message message = update.getMessage(); + if (message != null && message.hasText()) { + String text = message.getText(); + String[] parts = text.split(" ", 2); + if (parts[0].startsWith(Commands.WEATHERCOMMAND)) { + if (parts.length == 2) { + String citywithoutdescription = parts[1].split("-->", 2)[0].trim(); + String weather = WeatherService.getInstance().fetchWeatherForecast(citywithoutdescription, message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(weather); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else { + HashMap recentWeather = DatabaseManager.getInstance().getRecentWeather(message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + if (recentWeather.size() > 0) { + ReplyKeyboardMarkup replyKeyboardMarkup = new ReplyKeyboardMarkup(); + List> commands = new ArrayList<>(); + for (Map.Entry entry : recentWeather.entrySet()) { + List commandRow = new ArrayList<>(); + commandRow.add(Commands.WEATHERCOMMAND + " " + entry.getKey() + " --> " + entry.getValue()); + commands.add(commandRow); + } + replyKeyboardMarkup.setResizeKeyboard(true); + replyKeyboardMarkup.setOneTimeKeyboad(true); + replyKeyboardMarkup.setSelective(true); + replyKeyboardMarkup.setKeyboard(commands); + sendMessageRequest.setReplayMarkup(replyKeyboardMarkup); + sendMessageRequest.setText(CustomMessages.chooseFromRecentWeather); + } else { + sendMessageRequest.setText(CustomMessages.pleaseSendMeCityWeather); + ForceReply forceReply = new ForceReply(); + forceReply.setForceReply(true); + forceReply.setSelective(true); + sendMessageRequest.setReplayMarkup(forceReply); + } + sendMessageRequest.setReplayToMessageId(message.getMessageId()); + sendMessageRequest.setChatId(message.getChatId()); + Message sentMessage = SenderHelper.SendMessage(sendMessageRequest, TOKEN); + try { + listOfSentMessages.put(sentMessage.getMessageId(), FORECASTWEATHERID); + } catch(NullPointerException e) { + log.error(e); + } + } + } else if (parts[0].startsWith(Commands.CURRENTWEATHERCOMMAND)) { + if (parts.length == 2) { + String citywithoutdescription = parts[1].split("-->", 2)[0].trim(); + String weather = WeatherService.getInstance().fetchWeatherCurrent(citywithoutdescription, message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(weather); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else { + HashMap recentWeather = DatabaseManager.getInstance().getRecentWeather(message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + if (recentWeather.size() > 0) { + ReplyKeyboardMarkup replyKeyboardMarkup = new ReplyKeyboardMarkup(); + List> commands = new ArrayList<>(); + for (Map.Entry entry : recentWeather.entrySet()) { + List commandRow = new ArrayList<>(); + commandRow.add(Commands.CURRENTWEATHERCOMMAND + " " + entry.getKey() + " --> " + entry.getValue()); + commands.add(commandRow); + } + replyKeyboardMarkup.setResizeKeyboard(true); + replyKeyboardMarkup.setOneTimeKeyboad(true); + replyKeyboardMarkup.setSelective(true); + replyKeyboardMarkup.setKeyboard(commands); + sendMessageRequest.setReplayMarkup(replyKeyboardMarkup); + sendMessageRequest.setText(CustomMessages.chooseFromRecentWeather); + } else { + sendMessageRequest.setText(CustomMessages.pleaseSendMeCityWeather); + ForceReply forceReply = new ForceReply(); + forceReply.setForceReply(true); + forceReply.setSelective(true); + sendMessageRequest.setReplayMarkup(forceReply); + } + sendMessageRequest.setChatId(message.getChatId()); + sendMessageRequest.setReplayToMessageId(message.getMessageId()); + Message sentMessage = SenderHelper.SendMessage(sendMessageRequest, TOKEN); + try { + listOfSentMessages.put(sentMessage.getMessageId(), CURRENTWEATHERID); + } catch(NullPointerException e) { + log.error(e); + } + } + } else if (message.isReply() && listOfSentMessages.contains(message.getReplyToMessage().getMessageId())) { + SendMessage sendMessageRequest = new SendMessage(); + if (listOfSentMessages.remove(message.getReplyToMessage().getMessageId()) == CURRENTWEATHERID) { + String weather = WeatherService.getInstance().fetchWeatherCurrent(message.getText(), message.getFrom().getId()); + sendMessageRequest.setText(weather); + } else { + String weather = WeatherService.getInstance().fetchWeatherForecast(message.getText(), message.getFrom().getId()); + sendMessageRequest.setText(weather); + } + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } else if (parts[0].startsWith(Commands.help) || + (message.getText().startsWith(Commands.startCommand) || !message.isGroupMessage())) { + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(CustomMessages.helpWeather); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } else if (message != null && message.hasLocation()) { + String weather = WeatherService.getInstance().fetchWeatherForecastByLocation(message.getLocation().getLongitude(), + message.getLocation().getLatitude(), message.getFrom().getId()); + SendMessage sendMessageRequest = new SendMessage(); + sendMessageRequest.setText(weather); + sendMessageRequest.setChatId(message.getChatId()); + SenderHelper.SendMessage(sendMessageRequest, TOKEN); + } + } +} diff --git a/src/main/java/org/telegram/updatesreceivers/RestApi.java b/src/main/java/org/telegram/updatesreceivers/RestApi.java new file mode 100644 index 0000000..c12d89f --- /dev/null +++ b/src/main/java/org/telegram/updatesreceivers/RestApi.java @@ -0,0 +1,35 @@ +package org.telegram.updatesreceivers; + +import org.telegram.api.Update; +import org.telegram.updateshandlers.UpdatesCallback; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Rest api to for webhook callback function + * @date 20 of June of 2015 + */ +@Path("callback") +public class RestApi { + + private final UpdatesCallback callback; + + public RestApi(UpdatesCallback callback) { + this.callback = callback; + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public void updateReceived(Update update) { + this.callback.onUpdateReceived(update); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public String testReceived() { + return "Hi there"; + } +} diff --git a/src/main/java/org/telegram/updatesreceivers/UpdatesThread.java b/src/main/java/org/telegram/updatesreceivers/UpdatesThread.java new file mode 100644 index 0000000..348225c --- /dev/null +++ b/src/main/java/org/telegram/updatesreceivers/UpdatesThread.java @@ -0,0 +1,111 @@ +package org.telegram.updatesreceivers; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.telegram.BuildVars; +import org.telegram.api.Update; +import org.telegram.methods.Constants; +import org.telegram.methods.GetUpdates; +import org.telegram.updateshandlers.UpdatesCallback; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Thread to request updates with active wait + * @date 20 of June of 2015 + */ +public class UpdatesThread { + private final UpdatesCallback callback; + private final ReaderThread readerThread; + private int lastReceivedUpdate; + private String token; + + public UpdatesThread(String token, UpdatesCallback callback) { + this.token = token; + this.callback = callback; + this.lastReceivedUpdate = -1; + if (BuildVars.useWebHook) { + this.readerThread = null; + } else { + this.readerThread = new ReaderThread(); + this.readerThread.start(); + } + } + + private class ReaderThread extends Thread { + @Override + public void run() { + while(true) { + GetUpdates request = new GetUpdates(); + request.setOffset(lastReceivedUpdate+1); + CloseableHttpClient httpclient = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + String url = Constants.BASEURL + token + "/" + GetUpdates.PATH; + HttpGet httpGet = new HttpGet(url + request.getUrlParams()); + httpGet.addHeader("Content-type", "application/x-www-form-urlencoded"); + httpGet.addHeader("charset", "UTF-8"); + HttpResponse response; + try { + response = httpclient.execute(httpGet); + HttpEntity ht = response.getEntity(); + + BufferedHttpEntity buf = new BufferedHttpEntity(ht); + String responseContent = EntityUtils.toString(buf, "UTF-8"); + + try { + JSONObject jsonObject = new JSONObject(responseContent); + if (!jsonObject.getBoolean("ok")) { + throw new InvalidObjectException(jsonObject.toString()); + } + JSONArray jsonArray = jsonObject.getJSONArray("result"); + if (jsonArray.length() != 0) { + List updates = new ArrayList(); + for (int i = 0; i < jsonArray.length(); i++) { + Update update = new Update(jsonArray.getJSONObject(i)); + if (update.getUpdateId() > lastReceivedUpdate) { + lastReceivedUpdate = update.getUpdateId(); + } + updates.add(update); + + } + callback.onUpdatesReceived(updates); + } else { + try { + synchronized (this) { + this.wait(500); + } + } catch (InterruptedException e) { + e.printStackTrace(); + continue; + } + } + } catch (JSONException e) { + try { + synchronized (this) { + this.wait(500); + } + } catch (InterruptedException e1) { + e.printStackTrace(); + continue; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/org/telegram/updatesreceivers/Webhook.java b/src/main/java/org/telegram/updatesreceivers/Webhook.java new file mode 100644 index 0000000..3a4d974 --- /dev/null +++ b/src/main/java/org/telegram/updatesreceivers/Webhook.java @@ -0,0 +1,63 @@ +package org.telegram.updatesreceivers; + +import com.sun.jersey.api.json.JSONConfiguration; +import org.glassfish.grizzly.http.server.HttpServer; +import org.glassfish.grizzly.ssl.SSLContextConfigurator; +import org.glassfish.grizzly.ssl.SSLEngineConfigurator; +import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory; +import org.glassfish.jersey.jackson.JacksonFeature; +import org.glassfish.jersey.server.ResourceConfig; +import org.telegram.BuildVars; +import org.telegram.updateshandlers.UpdatesCallback; + +import java.io.IOException; +import java.net.URI; + +/** + * @author Ruben Bermudez + * @version 1.0 + * @brief Webhook to receive updates + * @date 20 of June of 2015 + */ +public class Webhook { + private static final String KEYSTORE_SERVER_FILE = "./keystore_server"; + private static final String KEYSTORE_SERVER_PWD = "asdfgh"; + + private final URI URL; + private final int port; + + public Webhook(UpdatesCallback callback, int webhookPort) { + this.port = webhookPort; + RestApi restApi = new RestApi(callback); + SSLContextConfigurator sslContext = new SSLContextConfigurator(); + + // set up security context + sslContext.setKeyStoreFile(KEYSTORE_SERVER_FILE); // contains server keypair + sslContext.setKeyStorePass(KEYSTORE_SERVER_PWD); + + ResourceConfig rc = new ResourceConfig(); + rc.register(restApi); + rc.register(JacksonFeature.class); + rc.property(JSONConfiguration.FEATURE_POJO_MAPPING, true); + + URL = getBaseURI(); + + final HttpServer grizzlyServer = GrizzlyHttpServerFactory.createHttpServer( + getBaseURI(), + rc, + true, + new SSLEngineConfigurator(sslContext).setClientMode(false).setNeedClientAuth(false)); + try { + grizzlyServer.start(); + } catch (IOException ignored) { + } + } + + private URI getBaseURI() { + return URI.create(BuildVars.INTERNALWEBHOOKURL + ":" + this.port); + } + + public String getURL() { + return String.format("%s:%d/callback", BuildVars.BASEWEBHOOKURL, this.port); + } + }