[
  {
    "path": ".gitignore",
    "content": "# Eclipse\n.externalToolBuilders/\n.settings/\nbin/\ntmp/\n.metadata\n.gradle\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.loadpath\n.project\n.classpath\n\n# IntelliJ IDES\n.idea/\nout/\n.idea_modules/\n*.iml\n*.ipr\n*.ids\n*.iws\n\n# OSX\n# Icon must end with two \\r\nIcon\n\n\n._*\n.DS_Store\n.AppleDouble\n.LSOverride\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n# Windows\n$RECYCLE.BIN/\nThumbs.db\nehthumbs.db\nDesktop.ini\n*.lnk\n\n# Linux\n*~\n.directory\n.Trash-*\n\n# Maven\ntarget/\npom.xml.tag\npom.xml.releaseBackup\npom.xml.versionsBackup\npom.xml.next\nrelease.properties\ndependency-reduced-pom.xml\nbuildNumber.properties\n.mvn/timing.properties\n\n# frontend-maven-plugin\nnode\nbower_components\nnode_modules\netc\n"
  },
  {
    "path": "README.md",
    "content": "social-sdk\n==========\n\nsocial-sdk是一个集成[新浪微博开放平台][1]、[QQ互联][2]、[腾讯微博开发平台][3]、[微信公众平台][4]等社交平台的接口的Java库。\n\n\n\n其实在开始这个项目之前，各个平台都已经提供相应的Java\nSDK，有官方的、也有非官方的开源项目，如如新浪微博开放平台有[weibo4j][5]。我也一直在使用这些项目。但是在使用过程中越到的问题越来越多，越来越麻烦，如：\n\n    -   这些SDK都是提供一个ZIP包，不适合Maven或Ivy管理的项目。\n\n    -   各个SDK都引入了开源公共类库，确改了包名，造成类库过多、混乱。\n\n    -   SDK更新超级慢，跟不上平台上接口的变更。\n\n    -   没有交流环境，遇到BUG找不到资料，找不到沟通的地方，需要自己去琢磨源代码。\n\n    -   ......\n\n因此，自己开发一个，尝试尽可能多的集成社交平台。\n\n\n\n**因为我并没有在项目中使用到那么多社交平台，所以有的社交平台没有可供测试的应用信息（通常叫做AppKey和AppSecret）。所以非常希望有资源的朋友共享测试帐号。**\n\n\n\n已实现\n---\n\n-   QQ相关\n\t-   通过QQ邮箱获取联系人，支持验证码自动登录\n\t-   获取QZone访客记录，支持验证码自动登录\n-   新浪微博\n\t-   新浪微博登录\n\t-   获取新浪微博用户信息\n-   QQ帐号/QQ互联\n\t-   支持http://wiki.connect.qq.com 列出的API\n-   腾讯微博（未测试）\n\t-   腾讯微博登录（未测试）\n\t-   获取腾讯微博用户信息（未测试）\n-   [微信帐号](../../wiki/微信API)\n\t-   [获取access_token](../../wiki/微信API#获取access token)\n\t-   [获取jsapi_ticket](../../wiki/微信API#获取jsapi_ticket)\n\t-   jsapi签名\n\t-   上传下载多媒体文件\n\t-   接受消息／事件推送／位置信息\n\t-   验证消息真实性\n\t-   发送被动响应信息\n\t-   发送客服消息\n\t-   发送模板消息\n\t-   分组管理\n\t-   获取用户基本信息\n\t-   获取关注者列表\n\t-   网页授权获取用户基本信息\n\t-   自定义菜单\n\t-   生成带参数的二维码接口\n\n\n\n计划开发\n----\n\n-   更多的接口\n\n-   支持更多的平台\n\n\n\n下载\n--\n\n推荐使用Maven下载。social-sdk已发布到Maven中央库。\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n<dependency>\n    <groupId>com.belerweb</groupId>\n    <artifactId>social-sdk</artifactId>\n    <version>0.0.5</version>\n</dependency>\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n\n\n参与\n--\n\n交流：GitHub上留言或加入QQ群（328171904）\n\n共享代码：Fork项目并提交Pull Requests\n\n提交BUG：直接在GitHub上提交\n\n其他：欢迎任何形式的贡献，文档、经验、意见...\n\n\n\n链接\n--\n\n[1]: <http://open.weibo.com/>\n[2]: <http://connect.qq.com/>\n[3]: <http://dev.t.qq.com/>\n[4]: <http://mp.weixin.qq.com/wiki/index.php>\n[5]: <http://code.google.com/p/weibo4j/>\n[6]: <https://github.com/belerweb/weibo4j>\n[7]: <https://github.com/belerweb/qq-connect>\n[8]: <https://github.com/belerweb/weixin-mp-sdk>\n"
  },
  {
    "path": "eclipse-java-google-style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"11\">\n<profile kind=\"CodeFormatterProfile\" name=\"GoogleStyle\" version=\"11\">\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_prefer_two_fragments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_comment_inline_tags\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_local_variable_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter\" value=\"16|-1|24\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16|5|80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.sort_type_annotations\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.5\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type_declaration\" value=\"569|-1|569\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_local_variable_annotation\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.sort_parameter_annotations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"16|3|49\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"100\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"32\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_type_annotation\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_field_declaration\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_generic_type_arguments\" value=\"16|-1|16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment_new_line_at_start_of_html_paragraph\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.sort_package_annotations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_parameter_annotation\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_member\" value=\"536|1|569\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16|4|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.force_if_else_statement_brace\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"3\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_package_annotation\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.sort_member_annotations\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"16|4|48\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.5\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_new_anonymous_class\" value=\"33\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"32|4|80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"100\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"32|4|81\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16|4|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.5\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"16|-1|16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"16|5|48\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.sort_local_variable_annotations\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_non_simple_member_annotation\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable\" value=\"569|-1|569\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package_declaration\" value=\"569|-1|569\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_for_statement\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n</profile>\n</profiles>\n"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\n\t<parent>\n\t\t<groupId>org.sonatype.oss</groupId>\n\t\t<artifactId>oss-parent</artifactId>\n\t\t<version>7</version>\n\t</parent>\n\n\t<groupId>com.belerweb</groupId>\n\t<artifactId>social-sdk</artifactId>\n\t<version>0.0.6-SNAPSHOT</version>\n\t<packaging>jar</packaging>\n\n\t<name>social-sdk</name>\n\t<description>All in one sdk, include weibo, qq connect, t.qq.com ...</description>\n\t<url>https://github.com/belerweb/social-sdk</url>\n\n\t<licenses>\n\t\t<license>\n\t\t\t<name>BSD</name>\n\t\t\t<url>http://opensource.org/licenses/bsd-license.html</url>\n\t\t\t<distribution>repo</distribution>\n\t\t</license>\n\t</licenses>\n\n\t<scm>\n\t\t<connection>scm:git:https://github.com/belerweb/social-sdk.git</connection>\n\t\t<developerConnection>scm:git:https://github.com/belerweb/social-sdk.git</developerConnection>\n\t\t<url>https://github.com/belerweb/social-sdk.git</url>\n\t</scm>\n\t<issueManagement>\n\t\t<system>Github Issue</system>\n\t\t<url>https://github.com/belerweb/social-sdk/issues</url>\n\t</issueManagement>\n\t<developers>\n\t\t<developer>\n\t\t\t<id>belerweb</id>\n\t\t\t<name>Tangjun He</name>\n\t\t\t<email>belerweb@gmail.com</email>\n\t\t\t<url>https://github.com/belerweb</url>\n\t\t</developer>\n\t</developers>\n\n\t<properties>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<maven.compiler.source>1.5</maven.compiler.source>\n\t\t<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>\n\t\t<dependency.commons-lang.version>2.6</dependency.commons-lang.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.json</groupId>\n\t\t\t<artifactId>json</artifactId>\n\t\t\t<version>20131018</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.httpcomponents</groupId>\n\t\t\t<artifactId>httpmime</artifactId>\n\t\t\t<version>4.3.1</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-lang</groupId>\n\t\t\t<artifactId>commons-lang</artifactId>\n\t\t\t<version>2.6</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t\t<version>2.4</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-net</groupId>\n\t\t\t<artifactId>commons-net</artifactId>\n\t\t\t<version>3.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jsoup</groupId>\n\t\t\t<artifactId>jsoup</artifactId>\n\t\t\t<version>1.7.3</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t<version>1.7.5</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-classic</artifactId>\n\t\t\t<version>1.0.13</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>4.11</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>com.googlecode.maven-java-formatter-plugin</groupId>\n\t\t\t\t<artifactId>maven-java-formatter-plugin</artifactId>\n\t\t\t\t<version>0.4</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<compilerCompliance>${maven.compiler.source}</compilerCompliance>\n\t\t\t\t\t<compilerSource>${maven.compiler.source}</compilerSource>\n\t\t\t\t\t<compilerTargetPlatform>${maven.compiler.source}</compilerTargetPlatform>\n\t\t\t\t\t<configFile>${project.basedir}/eclipse-java-google-style.xml</configFile>\n\t\t\t\t\t<lineEnding>LF</lineEnding>\n\t\t\t\t</configuration>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>format</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t</plugins>\n\t\t<pluginManagement>\n\t\t\t<plugins>\n\t\t\t\t<plugin>\n\t\t\t\t\t<groupId>org.eclipse.m2e</groupId>\n\t\t\t\t\t<artifactId>lifecycle-mapping</artifactId>\n\t\t\t\t\t<version>1.0.0</version>\n\t\t\t\t\t<configuration>\n\t\t\t\t\t\t<lifecycleMappingMetadata>\n\t\t\t\t\t\t\t<pluginExecutions>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>com.googlecode.maven-java-formatter-plugin</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>maven-java-formatter-plugin</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[0.4]</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>format</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<execute>\n\t\t\t\t\t\t\t\t\t\t\t<runOnIncremental>true</runOnIncremental>\n\t\t\t\t\t\t\t\t\t\t</execute>\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t\t<pluginExecution>\n\t\t\t\t\t\t\t\t\t<pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t\t\t\t\t<artifactId>maven-enforcer-plugin</artifactId>\n\t\t\t\t\t\t\t\t\t\t<versionRange>[1.0,)</versionRange>\n\t\t\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t\t\t<goal>enforce</goal>\n\t\t\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t\t</pluginExecutionFilter>\n\t\t\t\t\t\t\t\t\t<action>\n\t\t\t\t\t\t\t\t\t\t<execute />\n\t\t\t\t\t\t\t\t\t</action>\n\t\t\t\t\t\t\t\t</pluginExecution>\n\t\t\t\t\t\t\t</pluginExecutions>\n\t\t\t\t\t\t</lifecycleMappingMetadata>\n\t\t\t\t\t</configuration>\n\t\t\t\t</plugin>\n\t\t\t</plugins>\n\t\t</pluginManagement>\n\t</build>\n\n</project>\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/API.java",
    "content": "package com.belerweb.social;\n\nimport com.belerweb.social.qq.connect.api.QQConnect;\nimport com.belerweb.social.qq.t.api.QQT;\nimport com.belerweb.social.weibo.api.Weibo;\nimport com.belerweb.social.weixin.api.Weixin;\n\n\npublic abstract class API {\n\n  protected Weibo weibo;\n  protected Weixin weixin;\n  protected QQConnect connect;\n  protected QQT t;\n\n  protected API(Weibo weibo) {\n    this.weibo = weibo;\n  }\n\n  protected API(Weixin weixin) {\n    this.weixin = weixin;\n  }\n\n  protected API(QQConnect connect) {\n    this.connect = connect;\n  }\n\n  protected API(QQT t) {\n    this.t = t;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/SDK.java",
    "content": "package com.belerweb.social;\n\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.http.HttpException;\n\npublic abstract class SDK {\n\n  private final Charset defaultCharset;\n\n  public SDK() {\n    this(null);\n  }\n\n  public SDK(Charset defaultCharset) {\n    this.defaultCharset = defaultCharset;\n  }\n\n  public String get(String url, List<NameValuePair> params) {\n    try {\n      Http.setDefaultCharset(defaultCharset);\n      return Http.get(url, params);\n    } catch (HttpException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  public String get(String url) {\n    return get(url, null);\n  }\n\n  public String post(String url, HttpEntity postBody) {\n    try {\n      Http.setDefaultCharset(defaultCharset);\n      return Http.post(url, postBody);\n    } catch (HttpException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  public String post(String url, List<NameValuePair> params) {\n    try {\n      Http.setDefaultCharset(defaultCharset);\n      return Http.post(url, params, \"UTF-8\");\n    } catch (HttpException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  public String post(String url) {\n    return post(url, (HttpEntity) null);\n  }\n\n  public void addParameter(List<NameValuePair> params, String name, Object value) {\n    if (value == null) {\n      throw new SocialException(\"Parameter \" + name + \" must not be null.\");\n    }\n    params.add(new BasicNameValuePair(name, value.toString()));\n  }\n\n  public void addNotNullParameter(List<NameValuePair> params, String name, Object value) {\n    if (value != null) {\n      params.add(new BasicNameValuePair(name, value.toString()));\n    }\n  }\n\n  public void addTrueParameter(List<NameValuePair> params, String name, Boolean value) {\n    if (Boolean.TRUE.equals(value)) {\n      params.add(new BasicNameValuePair(name, value.toString()));\n    }\n  }\n\n  /**\n   * 经纬度转换为地址\n   * \n   * @param lon 经度\n   * @param lat 纬度\n   */\n  public Result<String> lonLatToAddress(Double lon, Double lat) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"sensor\", \"false\");\n    addParameter(params, \"language\", \"zh\");\n    addParameter(params, \"latlng\", lat + \",\" + lon);\n    String json = get(\"https://maps.googleapis.com/maps/api/geocode/json\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    if (!\"OK\".equals(jsonObject.getString(\"status\"))) {\n      Error error = new Error();\n      error.setErrorCode(jsonObject.getString(\"status\"));\n      error.setError(jsonObject.optString(\"error_message\"));\n      return new Result<String>(error);\n    }\n\n    JSONArray results = jsonObject.getJSONArray(\"results\");\n    if (results.length() == 0) {\n      return new Result<String>(StringUtils.EMPTY);\n    }\n    return new Result<String>(results.getJSONObject(0).getString(\"formatted_address\"));\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/bean/Error.java",
    "content": "package com.belerweb.social.bean;\n\nimport org.json.JSONObject;\n\npublic final class Error extends JsonBean {\n\n  private String request;\n  private String errorCode;\n  private String error;\n\n  public Error() {}\n\n  public Error(String code, String message) {\n    this.errorCode = code;\n    this.error = message;\n  }\n\n  private Error(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  public String getRequest() {\n    return request;\n  }\n\n  public void setRequest(String request) {\n    this.request = request;\n  }\n\n  public String getErrorCode() {\n    return errorCode;\n  }\n\n  public void setErrorCode(String errorCode) {\n    this.errorCode = errorCode;\n  }\n\n  public String getError() {\n    return error;\n  }\n\n  public void setError(String error) {\n    this.error = error;\n  }\n\n  @Override\n  public String toString() {\n    return errorCode + \":\" + error + \"(\" + request + \")\";\n  }\n\n  public static Error parse(JSONObject jsonObject) {\n    String errorCode = jsonObject.optString(\"error_code\", null);\n    if (errorCode != null) {// 微博\n      String request = jsonObject.optString(\"request\", null);\n      String error = jsonObject.optString(\"error\", null);\n      Error er = new Error(jsonObject);\n      er.setRequest(request);\n      er.setErrorCode(errorCode);\n      er.setError(error);\n      return er;\n    }\n\n    errorCode = jsonObject.optString(\"error\", null);\n    if (errorCode != null) {// QQ互联\n      String error = jsonObject.optString(\"error_description\", null);\n      Error er = new Error(jsonObject);\n      er.setErrorCode(errorCode);\n      er.setError(error);\n      return er;\n    }\n\n    Integer ret = Result.parseInteger(jsonObject.opt(\"ret\"));\n    if (ret != null && ret != 0) {// QQ互联\n      String msg = jsonObject.optString(\"msg\", null);\n      Error er = new Error(jsonObject);\n      er.setErrorCode(ret.toString());\n      er.setError(msg);\n      return er;\n    }\n\n    ret = Result.parseInteger(jsonObject.opt(\"errcode\"));\n    if (ret != null && ret != 0) {// 微信\n      String error = jsonObject.optString(\"errmsg\", null);\n      Error er = new Error(jsonObject);\n      er.setErrorCode(ret.toString());\n      er.setError(error);\n      return er;\n    }\n\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/bean/Gender.java",
    "content": "package com.belerweb.social.bean;\n\npublic enum Gender {\n\n  MALE(1, \"m\", \"男\", \"Male\"), FEMALE(0, \"f\", \"女\", \"Female\"), UNKNOWN(-1, \"n\", \"未知\", \"Unknown\");\n\n  int intValue;\n  String code;\n  String zhValue;\n  String enValue;\n\n  private Gender(int intValue, String code, String zhValue, String enValue) {\n    this.intValue = intValue;\n    this.code = code;\n    this.zhValue = zhValue;\n    this.enValue = zhValue;\n  }\n\n  public int value() {\n    return intValue;\n  }\n\n  public String code() {\n    return code;\n  }\n\n  public String text() {\n    return zhValue;\n  }\n\n  public String enText() {\n    return enValue;\n  }\n\n  @Override\n  public String toString() {\n    return zhValue;\n  }\n\n  public static Gender parse(Integer val) {\n    if (val == null) {\n      return null;\n    }\n    if (new Integer(1).equals(val)) {\n      return MALE;\n    }\n    if (new Integer(0).equals(val) || new Integer(2).equals(val)) {\n      // FIXME 微信2代表女性，0表示未知。暂且把微信的未知看作女性，如同新浪微博用户没有设置性别的时候会返回男性一样\n      return FEMALE;\n    }\n    return UNKNOWN;\n  }\n\n  public static Gender parse(String val) {\n    if (val == null) {\n      return null;\n    }\n    if (\"男\".equals(val) || \"m\".equalsIgnoreCase(val) || \"male\".equalsIgnoreCase(val)\n        || \"b\".equalsIgnoreCase(val) || \"boy\".equalsIgnoreCase(val)) {\n      return MALE;\n    }\n    if (\"女\".equals(val) || \"f\".equalsIgnoreCase(val) || \"female\".equalsIgnoreCase(val)\n        || \"g\".equalsIgnoreCase(val) || \"girl\".equalsIgnoreCase(val)) {\n      return FEMALE;\n    }\n    return UNKNOWN;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/bean/JsonBean.java",
    "content": "package com.belerweb.social.bean;\n\nimport org.json.JSONObject;\n\npublic abstract class JsonBean {\n\n  private JSONObject jsonObject;\n\n  protected JsonBean() {}\n\n  protected JsonBean(JSONObject jsonObject) {\n    this.jsonObject = jsonObject;\n  }\n\n  public JSONObject getJsonObject() {\n    return jsonObject;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/bean/OnlineStatus.java",
    "content": "package com.belerweb.social.bean;\n\npublic enum OnlineStatus {\n\n  ONLINE(1, \"在线\", \"online\"), OFFLINE(0, \"不在线\", \"offline\");\n\n  private int status;\n  private String text;\n  private String enText;\n\n  private OnlineStatus(int status, String text, String enText) {\n    this.status = status;\n    this.text = text;\n    this.enText = enText;\n  }\n\n  public boolean online() {\n    return status == 1;\n  }\n\n  public int status() {\n    return status;\n  }\n\n  public String text() {\n    return text;\n  }\n\n  public String enText() {\n    return enText;\n  }\n\n  @Override\n  public String toString() {\n    return super.toString();\n  }\n\n  public static OnlineStatus parse(Integer val) {\n    if (val == null) {\n      return null;\n    }\n    if (new Integer(1).equals(val)) {\n      return ONLINE;\n    }\n    return OFFLINE;\n  }\n\n  public static OnlineStatus parse(String val) {\n    if (val == null) {\n      return null;\n    }\n    if (\"在线\".equals(val) || \"online\".equalsIgnoreCase(val)) {\n      return ONLINE;\n    }\n    return OFFLINE;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/bean/Result.java",
    "content": "package com.belerweb.social.bean;\n\nimport java.lang.reflect.Method;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.exception.SocialException;\n\npublic class Result<T> {\n\n  private Error error;\n  private T result;\n  private List<T> results;\n\n  public Result(Error error) {\n    this.error = error;\n  }\n\n  public Result(T result) {\n    this.result = result;\n  }\n\n  public Result(List<T> results) {\n    this.results = results;\n  }\n\n  public boolean success() {\n    return error == null;\n  }\n\n  public Error getError() {\n    return error;\n  }\n\n  public T getResult() {\n    return result;\n  }\n\n  public List<T> getResults() {\n    return results;\n  }\n\n  public static <T> Result<T> parse(String json, Class<T> resultType) {\n    try {\n      if (json.matches(\"^\\\\s*\\\\[.*$\")) {\n        return new Result<T>(parse(new JSONArray(json), resultType));\n      } else {\n        return parse(new JSONObject(json), resultType);\n      }\n    } catch (Exception e) {\n      throw new SocialException(e);\n    }\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public static <T> Result<T> parse(JSONObject jsonObject, Class<T> resultType) {\n    try {\n      Error error = Error.parse(jsonObject);\n      if (error == null) {\n        Method parse = resultType.getMethod(\"parse\", JSONObject.class);\n        T obj = (T) parse.invoke(null, jsonObject);\n        return new Result<T>(obj);\n      }\n      return new Result<T>(error);\n    } catch (Exception e) {\n      throw new SocialException(e);\n    }\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public static <T> List<T> parse(JSONArray jsonArray, Class<T> resultType) {\n    List<T> list = new ArrayList<T>();\n    if (jsonArray == null) {\n      return list;\n    }\n    try {\n      for (int i = 0; i < jsonArray.length(); i++) {\n        if (resultType.isAssignableFrom(String.class)) {\n          list.add((T) toString(jsonArray.get(i)));\n        } else if (resultType.isAssignableFrom(Integer.class)) {\n          list.add((T) parseInteger(jsonArray.get(i)));\n        } else if (resultType.isAssignableFrom(Long.class)) {\n          list.add((T) parseLong(jsonArray.get(i)));\n        } else if (resultType.isAssignableFrom(Double.class)) {\n          list.add((T) parseDouble(jsonArray.get(i)));\n        } else {\n          Method parse = resultType.getMethod(\"parse\", JSONObject.class);\n          list.add((T) parse.invoke(null, jsonArray.getJSONObject(i)));\n        }\n      }\n      return list;\n    } catch (Exception e) {\n      throw new SocialException(e);\n    }\n  }\n\n  public static String toString(Object obj) {\n    if (obj == null) {\n      return null;\n    }\n\n    return obj.toString();\n  }\n\n  public static Long parseLong(Object obj) {\n    if (obj == null) {\n      return null;\n    }\n\n    Long result = null;\n    if (obj instanceof Number) {\n      result = ((Number) obj).longValue();\n    } else if (obj instanceof String) {\n      result = Long.valueOf((String) obj);\n    }\n\n    return result;\n  }\n\n  public static Integer parseInteger(Object obj) {\n    if (obj == null) {\n      return null;\n    }\n\n    Integer result = null;\n    if (obj instanceof Number) {\n      result = ((Number) obj).intValue();\n    } else if (obj instanceof String) {\n      result = Integer.valueOf((String) obj);\n    }\n\n    return result;\n  }\n\n  public static Double parseDouble(Object obj) {\n    if (obj == null) {\n      return null;\n    }\n\n    Double result = null;\n    if (obj instanceof Number) {\n      result = ((Number) obj).doubleValue();\n    } else if (obj instanceof String) {\n      result = Double.valueOf((String) obj);\n    }\n\n    return result;\n  }\n\n  public static Boolean parseBoolean(Object obj) {\n    if (obj == null) {\n      return null;\n    }\n\n    Boolean result = null;\n    if (obj instanceof Boolean) {\n      result = (Boolean) obj;\n    } else if (obj instanceof Integer) {\n      result = ((Integer) obj).intValue() == 1;\n    } else if (obj instanceof String) {\n      result = Boolean.valueOf(obj.toString());\n    }\n\n    return result;\n  }\n\n  public static Date parseDate(Object obj, String pattern, Locale locale) {\n    if (obj == null) {\n      return null;\n    }\n\n    Date result = null;\n    if (obj instanceof Date) {\n      result = new Date(((Date) obj).getTime());\n    } else if (obj instanceof String) {\n      try {\n        SimpleDateFormat format = new SimpleDateFormat(pattern, locale);\n        result = format.parse((String) obj);\n      } catch (ParseException e) {\n        throw new SocialException(e);\n      }\n    }\n\n    return result;\n  }\n\n  public static Date parseTimeSeconds(Object obj) {\n    Integer seconds = Result.parseInteger(obj);\n    if (seconds == null || seconds == 0) {\n      return null;\n    }\n    return new Date(seconds * 1000);\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/captcha/api/Yundama.java",
    "content": "package com.belerweb.social.captcha.api;\n\nimport java.io.IOException;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.entity.mime.MultipartEntityBuilder;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.captcha.bean.YundamaType;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.http.HttpException;\n\npublic class Yundama {\n\n  private String appId = \"85\";\n  private String appKey = \"19fcd07d8de9c03b8cebec5d8bfe7d8e\";\n  private String username;\n  private String password;\n\n  public Yundama(String username, String password) {\n    this.username = username;\n    this.password = password;\n  }\n\n  public Result<String> decode(byte[] img, YundamaType type) {\n    HttpPost request = new HttpPost(\"http://api.yundama.com/api.php?method=upload\");\n    MultipartEntityBuilder builder =\n        MultipartEntityBuilder.create()\n            .addBinaryBody(\"file\", img, ContentType.create(\"image/png\"), \"code.png\")\n            .addTextBody(\"username\", username).addTextBody(\"password\", password)\n            .addTextBody(\"codetype\", type.getType().toString()).addTextBody(\"appid\", appId)\n            .addTextBody(\"appkey\", appKey).addTextBody(\"timeout\", \"60\");\n    request.setEntity(builder.build());\n    try {\n      HttpResponse response = Http.CLIENT.execute(request);\n      String json = IOUtils.toString(response.getEntity().getContent());\n      request.releaseConnection();\n      JSONObject jsonObject = new JSONObject(json);\n      Integer ret = Result.parseInteger(jsonObject.get(\"ret\"));\n      if (ret == 0) {\n        String url =\n            \"http://api.yundama.com/api.php?method=result&cid=\"\n                + Result.toString(jsonObject.get(\"cid\"));\n        long start = System.currentTimeMillis();\n        while (true) {\n          jsonObject = new JSONObject(Http.get(url));\n          if (Result.parseInteger(jsonObject.get(\"ret\")) == 0) {\n            return new Result<String>(Result.toString(jsonObject.get(\"text\")));\n          }\n          if (System.currentTimeMillis() - start > 10000) {\n            return new Result<String>(new Error(\"TIMEOUT\", \"验证码识别超时。\"));\n          }\n          try {\n            Thread.sleep(300);\n          } catch (InterruptedException e) {\n            e.printStackTrace();\n          }\n        }\n      }\n      Error error = new Error();\n      error.setErrorCode(ret.toString());\n      return new Result<String>(error);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (HttpException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/captcha/bean/YundamaType.java",
    "content": "package com.belerweb.social.captcha.bean;\n\n\npublic enum YundamaType {\n  /**\n   * 不定长英文数字 2.5题分一个字符（按文本长度收费）\n   */\n  ALPHANUMERIC(1000),\n  /**\n   * 1位英文数字 7题分\n   */\n  ALPHANUMERIC1(1001),\n  /**\n   * 2位英文数字 8题分\n   */\n  ALPHANUMERIC2(1002),\n  /**\n   * 3位英文数字 9题分\n   */\n  ALPHANUMERIC3(1003),\n  /**\n   * 4位英文数字 10题分\n   */\n  ALPHANUMERIC4(1004),\n  /**\n   * 5位英文数字 12题分\n   */\n  ALPHANUMERIC5(1005),\n  /**\n   * 6位英文数字 15题分\n   */\n  ALPHANUMERIC6(1006),\n  /**\n   * 7位英文数字 17题分\n   */\n  ALPHANUMERIC7(1007),\n  /**\n   * 8位英文数字 20题分\n   */\n  ALPHANUMERIC8(1008),\n  /**\n   * 9位英文数字 22题分\n   */\n  ALPHANUMERIC9(1009),\n  /**\n   * 10位英文数字 25题分\n   */\n  ALPHANUMERIC10(1010),\n  /**\n   * 11位英文数字 27题分\n   */\n  ALPHANUMERIC11(1011),\n  /**\n   * 12位英文数字 30题分\n   */\n  ALPHANUMERIC12(1012),\n  /**\n   * 13位英文数字 32题分\n   */\n  ALPHANUMERIC13(1013),\n  /**\n   * 14位英文数字 35题分\n   */\n  ALPHANUMERIC14(1014),\n  /**\n   * 15位英文数字 37题分\n   */\n  ALPHANUMERIC15(1015),\n  /**\n   * 16位英文数字 40题分\n   */\n  ALPHANUMERIC16(1016),\n  /**\n   * 17位英文数字 42题分\n   */\n  ALPHANUMERIC17(1017),\n  /**\n   * 18位英文数字 45题分\n   */\n  ALPHANUMERIC18(1018),\n  /**\n   * 19位英文数字 47题分\n   */\n  ALPHANUMERIC19(1019),\n  /**\n   * 20位英文数字 50题分\n   */\n  ALPHANUMERIC20(1020),\n  /**\n   * 2位纯汉字 20题分\n   */\n  CHINESE2(2002),\n  /**\n   * 4位纯汉字 40题分\n   */\n  CHINESE4(2004),\n  /**\n   * 4位纯英文 10题分\n   */\n  ALPHABETIC4(3004),\n  /**\n   * 5位纯英文 12题分\n   */\n  ALPHABETIC5(3005),\n  /**\n   * 6位纯英文 15题分\n   */\n  ALPHABETIC6(3006),\n  /**\n   * 4位纯数字 10题分\n   */\n  NUMERIC4(4004),\n  /**\n   * 5位纯数字 12题分\n   */\n  NUMERIC5(4005);\n\n  Integer type;\n\n  YundamaType(Integer type) {\n    this.type = type;\n  }\n\n  public Integer getType() {\n    return type;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/exception/SocialException.java",
    "content": "package com.belerweb.social.exception;\n\npublic class SocialException extends RuntimeException {\n\n  private static final long serialVersionUID = 3536584215181288508L;\n\n  public SocialException(Exception exception) {\n    super(exception);\n  }\n\n  public SocialException(String message) {\n    super(message);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/http/Http.java",
    "content": "package com.belerweb.social.http;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.Charset;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.net.ssl.SSLContext;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.apache.http.Header;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.config.RequestConfig;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpRequestBase;\nimport org.apache.http.conn.ssl.SSLContexts;\nimport org.apache.http.conn.ssl.TrustStrategy;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.impl.client.HttpClientBuilder;\n\npublic final class Http {\n\n  private static final String[] AGENTS =\n      new String[] {\n          \"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36\",\n          \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36\",\n          \"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.66 Safari/535.11\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; Media Center PC 5.0; .NET CLR 3.5.21022; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; InfoPath.2; .NET CLR 3.0.30729; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB6.3; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; GTB0.0; InfoPath.1; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 4.0.20506; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; InfoPath.2; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727; InfoPath.1; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; GreenBrowser)\",\n          \"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 1.1.4322; GreenBrowser)\",\n          \"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36\"};\n\n\n  public static final HttpClient CLIENT = newClient();\n\n  private static final ThreadLocal<Charset> defaultCharset = new ThreadLocal<Charset>();\n\n  private static Charset getDefaultCharset() {\n    return defaultCharset.get();\n  }\n\n  /**\n   * Sets the default charset for reading the response, which is used while the server side does not\n   * provided the encoding or charset. If {@code null} is set, the {@link Charset#defaultCharset()}\n   * will be used to read the response. Note, this is a {@link ThreadLocal} variable.\n   * \n   * @param charset the charset.\n   * @see #responseToString(HttpResponse)\n   */\n  public static void setDefaultCharset(Charset charset) {\n    defaultCharset.set(charset);\n  }\n\n  public static String get(String uri, List<NameValuePair> params) throws HttpException {\n    String url = uri;\n    if (params != null) {\n      String param = StringUtils.join(params, \"&\");\n      if (url.contains(\"?\")) {\n        url = url + \"&\" + param;\n      } else {\n        url = url + \"?\" + param;\n      }\n    }\n\n    return get(url);\n  }\n\n  public static String get(String uri, Header... headers) throws HttpException {\n    HttpGet request = new HttpGet(uri);\n    if (headers != null) {\n      for (Header header : headers) {\n        request.addHeader(header);\n      }\n    }\n    return execute(request);\n  }\n\n  public static String post(String uri, HttpEntity postBody, Header... headers)\n      throws HttpException {\n    HttpPost request = new HttpPost(uri);\n    if (postBody != null) {\n      request.setEntity(postBody);\n    }\n\n    if (headers != null) {\n      for (Header header : headers) {\n        request.addHeader(header);\n      }\n    }\n    return execute(request);\n  }\n\n  public static String post(String uri, List<NameValuePair> params, String charset,\n      Header... headers) throws HttpException {\n    HttpPost request = new HttpPost(uri);\n    if (params != null) {\n      List<NameValuePair> parameters = new ArrayList<NameValuePair>();\n      parameters.addAll(params);\n      try {\n        HttpEntity entity = new UrlEncodedFormEntity(parameters, charset);\n        request.setEntity(entity);\n      } catch (UnsupportedEncodingException e) {\n        throw new HttpException(e);\n      }\n    }\n\n    if (headers != null) {\n      for (Header header : headers) {\n        request.addHeader(header);\n      }\n    }\n    return execute(request);\n  }\n\n  public static String post(String uri) throws HttpException {\n    return post(uri, (HttpEntity) null);\n  }\n\n  private static String execute(HttpRequestBase request) throws HttpException {\n    try {\n      HttpResponse response = CLIENT.execute(request);\n      // StatusLine status = response.getStatusLine();\n      return responseToString(response);\n      // if (status.getStatusCode() != HttpStatus.SC_OK) {\n      // throw new HttpException(status.getStatusCode() + \":\" + status.getReasonPhrase() + \"\\r\\n\"\n      // + result);\n      // }\n    } catch (ClientProtocolException e) {\n      throw new HttpException(e);\n    } catch (IOException e) {\n      throw new HttpException(e);\n    } finally {\n      request.releaseConnection();\n    }\n  }\n\n  public static boolean isRequestSuccess(HttpResponse response) throws HttpException {\n    return response.getStatusLine() != null\n        && response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;\n  }\n\n  public static String responseToString(HttpResponse response) throws HttpException {\n    HttpEntity entity = response.getEntity();\n    String result = null;\n    if (entity != null) {\n      Charset charset = null;\n      Header encoding = entity.getContentEncoding();\n      if (encoding == null) {\n        ContentType contentType = ContentType.get(entity);\n        if (contentType != null) {\n          charset = contentType.getCharset();\n        }\n      } else {\n        charset = Charset.forName(encoding.getValue());\n      }\n      if (charset == null) {\n        charset = getDefaultCharset();\n      }\n      try {\n        result = IOUtils.toString(entity.getContent(), charset);\n      } catch (Exception e) {\n        throw new HttpException(e);\n      }\n      return result;\n    } else {\n      throw new HttpException(\"No response entity.\");\n    }\n  }\n\n  public static String randomAgent() {\n    return AGENTS[RandomUtils.nextInt(AGENTS.length)];\n  }\n\n  public static HttpClient newClient() {\n    SSLContext sslContext = SSLContexts.createDefault();\n    try {\n      sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {\n        public boolean isTrusted(X509Certificate[] chain, String authType)\n            throws CertificateException {\n          return true;\n        }\n      }).build();\n    } catch (Exception e) {\n      e.printStackTrace();\n    }\n    return HttpClientBuilder\n        .create()\n        .setSslcontext(sslContext)\n        .setMaxConnPerRoute(50)\n        .setMaxConnTotal(200)\n        .setUserAgent(Http.randomAgent())\n        .setDefaultRequestConfig(\n            RequestConfig.custom().setConnectTimeout(30000).setSocketTimeout(30000)\n                .setConnectionRequestTimeout(30000).build()).build();\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/http/HttpException.java",
    "content": "package com.belerweb.social.http;\n\n\n\npublic class HttpException extends Exception {\n\n  private static final long serialVersionUID = -7528165403129614352L;\n\n  public HttpException(Exception exception) {\n    super(exception);\n  }\n\n  public HttpException(String message) {\n    super(message);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/mail/api/POP3.java",
    "content": "package com.belerweb.social.mail.api;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.net.SocketException;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.net.pop3.POP3Client;\nimport org.apache.commons.net.pop3.POP3MessageInfo;\nimport org.apache.commons.net.pop3.POP3SClient;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.exception.SocialException;\n\n/**\n * POP3 邮件工具\n */\npublic class POP3 {\n\n  private static final Logger LOGGER = LoggerFactory.getLogger(POP3.class);\n\n  private String username;\n  private String password;\n  private String host;\n  private int port;\n\n  private POP3Client client;\n\n  public POP3(String username, String password, String host) {\n    this(username, password, host, org.apache.commons.net.pop3.POP3.DEFAULT_PORT, false);\n  }\n\n  public POP3(String username, String password, String host, int port, boolean ssl) {\n    this.username = username;\n    this.password = password;\n    this.host = host;\n    this.port = port;\n    if (ssl) {\n      this.client = new POP3SClient(\"SSL\", true);\n    } else {\n      this.client = new POP3Client();\n    }\n    this.client.setDefaultTimeout(300000);\n  }\n\n  private boolean login() throws SocketException, IOException {\n    client.connect(host, port);\n    return client.login(username, password);\n  }\n\n  /**\n   * 检查用户信息是否正确\n   */\n  public boolean test() throws SocialException {\n    try {\n      return login();\n    } catch (SocketException e) {\n      e.printStackTrace();\n      throw new SocialException(e);\n    } catch (IOException e) {\n      e.printStackTrace();\n      throw new SocialException(e);\n    } finally {\n      try {\n        this.client.disconnect();\n      } catch (IOException e) {\n        e.printStackTrace();\n      }\n    }\n  }\n\n  /**\n   * 下载所有电子邮件到指定目录\n   */\n  public void download(File dir) throws SocialException {\n    if (!dir.isDirectory() || !dir.canWrite()) {\n      throw new SocialException(\"The specified directory is unavailable.\");\n    }\n\n    try {\n      if (login()) {\n        POP3MessageInfo[] messages = client.listUniqueIdentifiers();\n        if (messages == null) {\n          LOGGER.debug(\"Could not retrieve message list.\");\n          throw new SocialException(\"Could not retrieve message list.\");\n\n        } else {\n          for (POP3MessageInfo message : messages) {\n            File eml = new File(dir, username + \"@\" + host + \"/\" + message.identifier + \".eml\");\n            try {\n              Reader reader = client.retrieveMessage(message.number);\n              if (reader == null) {\n                LOGGER.debug(\"Could not retrieve message.\");\n                continue;\n              }\n              if (eml.exists()\n                  && ((message.size > 0 && eml.length() == message.size) || eml.length() > 1000)) {\n                LOGGER.debug(\"Message {} exist, skip download.\", message.identifier);\n                continue;\n              }\n              eml.getParentFile().mkdirs();\n              LOGGER.debug(\"Downloading {} ...\", message.identifier);\n              IOUtils.copy(reader, new FileOutputStream(eml));;\n              LOGGER.debug(\"Downloaded {} ...\", message.identifier);\n            } catch (Exception e) {\n              try {\n                FileUtils.forceDelete(eml);\n              } catch (Exception exception) {\n                e.printStackTrace();\n              }\n            }\n          }\n        }\n      }\n    } catch (SocketException e) {\n      e.printStackTrace();\n    } catch (IOException e) {\n      e.printStackTrace();\n    } finally {\n      try {\n        client.disconnect();\n      } catch (IOException e) {\n        e.printStackTrace();\n      }\n    }\n\n  }\n\n  /**\n   * 下载所有电子邮件到指定目录\n   */\n  public void download(String dir) {\n    download(new File(dir));\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/api/OAuth2.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.qq.connect.bean.AccessToken;\nimport com.belerweb.social.qq.connect.bean.Display;\nimport com.belerweb.social.qq.connect.bean.Gut;\nimport com.belerweb.social.qq.connect.bean.OpenID;\nimport com.belerweb.social.qq.connect.bean.Scope;\n\npublic final class OAuth2 extends API {\n\n  OAuth2(QQConnect connect) {\n    super(connect);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * @see OAuth2#authorize(Boolean)\n   */\n  public String authorize() {\n    return authorize(false);\n  }\n\n\n  /**\n   * 获取Authorization Code\n   * \n   * @see OAuth2#authorize(String, Boolean)\n   */\n  public String authorize(String redirectUri) {\n    return authorize(redirectUri, false);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link QQConnect} 从获取clientId，redirectUri，responseType为code，state使用authorize，scope使用\n   * {@link Scope#ALL}，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, Scope[], Display, Gut, Boolean)\n   */\n  public String authorize(Boolean wap) {\n    return authorize(connect.getRedirectUri(), wap);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link QQConnect} 从获取clientId，responseType为code，state使用authorize，scope使用 {@link Scope#ALL}\n   * ，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, Scope[], Display, Gut, Boolean)\n   */\n  public String authorize(String redirectUri, Boolean wap) {\n    return authorize(connect.getClientId(), redirectUri, \"code\", \"authorize\", Scope.ALL, null,\n        null, wap);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 文档地址：http://wiki.connect.qq.com/使用authorization_code获取access_token\n   * \n   * @param clientId 必须，申请QQ登录成功后，分配给应用的appid。\n   * @param redirectUri 必须，成功授权后的回调地址，必须是注册appid时填写的主域名下的地址，建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode。\n   * @param responseType 必须，授权类型，此值固定为“code”。\n   * @param state 必须，client端的状态值。用于第三方应用防止CSRF攻击，成功授权后回调时会原样带回。请务必严格按照流程检查用户与state参数状态的绑定。\n   * @param scope 可选，请求用户授权时向用户显示的可进行授权的列表\n   * @param display 可选，仅PC网站接入时使用。用于展示的样式。不传则默认展示为PC下的样式。如果传入“mobile”，则展示为mobile端下的样式。\n   * @param gut 仅WAP网站接入时使用。QQ登录页面版本（1：wml版本； 2：xhtml版本），默认值为1。\n   * @param wap 是否使wap版，默认为false\n   */\n  public String authorize(String clientId, String redirectUri, String responseType, String state,\n      Scope[] scope, Display display, Gut gut, Boolean wap) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"response_type\", responseType);\n    connect.addParameter(params, \"client_id\", clientId);\n    connect.addParameter(params, \"redirect_uri\", redirectUri);\n    connect.addParameter(params, \"state\", state);\n    if (scope != null) {\n      connect.addParameter(params, \"scope\", StringUtils.join(scope, \",\"));\n    }\n    if (Display.MOBILE.equals(display)) {\n      connect.addParameter(params, \"display\", \"mobile\");\n    }\n    if (Gut.XHTML.equals(gut)) {\n      connect.addParameter(params, \"g_ut\", gut.toString());\n    }\n\n    String url = \"https://graph.qq.com/oauth2.0/authorize?\";\n    if (Boolean.TRUE.equals(wap)) {\n      url = \"https://graph.z.qq.com/moc2/authorize?\";\n    }\n    return url + StringUtils.join(params, \"&\");\n  }\n\n  /**\n   * 通过Authorization Code获取Access Token，此接口适用于PC网站。\n   * \n   * 从 {@link QQConnect} 从获取clientId，clientSecret，redirectUri，grantType为authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String, Boolean)\n   */\n  public Result<AccessToken> accessToken(String code) {\n    return accessToken(code, connect.getRedirectUri());\n  }\n\n  /**\n   * 通过Authorization Code获取Access Token，此接口适用于PC网站。\n   * \n   * 从 {@link QQConnect} 从获取clientId，clientSecret，grantType为authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String, Boolean)\n   */\n  public Result<AccessToken> accessToken(String code, String redirectUri) {\n    return accessToken(code, redirectUri, null);\n  }\n\n  /**\n   * 通过Authorization Code获取Access Token\n   * \n   * 从 {@link QQConnect} 从获取clientId，clientSecret，redirectUri，grantType为authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String, Boolean)\n   */\n  public Result<AccessToken> accessToken(String code, Boolean wap) {\n    return accessToken(code, connect.getRedirectUri(), wap);\n  }\n\n  /**\n   * 通过Authorization Code获取Access Token\n   * \n   * 从 {@link QQConnect} 从获取clientId，clientSecret，grantType为authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String, Boolean)\n   */\n  public Result<AccessToken> accessToken(String code, String redirectUri, Boolean wap) {\n    return accessToken(connect.getClientId(), connect.getClientSecret(), \"authorization_code\",\n        code, redirectUri, wap);\n  }\n\n  /**\n   * 通过Authorization Code获取Access Token\n   * \n   * 文档地址：http://wiki.connect.qq.com/使用authorization_code获取access_token\n   * \n   * @param clientId 申请QQ登录成功后，分配给网站的appid。\n   * @param clientSecret 申请QQ登录成功后，分配给网站的appkey。\n   * @param grantType 授权类型，在本步骤中，此值为“authorization_code”。\n   * @param code 上一步返回的authorization code。如果用户成功登录并授权，则会跳转到指定的回调地址，并在URL中带上Authorization\n   *        Code。注意此code会在10分钟内过期。\n   * @param redirectUri 与上面一步中传入的redirect_uri保持一致。\n   * @param wap 是否使wap版，默认为false\n   */\n  public Result<AccessToken> accessToken(String clientId, String clientSecret, String grantType,\n      String code, String redirectUri, Boolean wap) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"client_id\", clientId);\n    connect.addParameter(params, \"client_secret\", clientSecret);\n    connect.addParameter(params, \"grant_type\", grantType);\n    connect.addParameter(params, \"code\", code);\n    connect.addParameter(params, \"redirect_uri\", redirectUri);\n    String url = \"https://graph.qq.com/oauth2.0/token\";\n    if (Boolean.TRUE.equals(wap)) {\n      url = \"https://graph.z.qq.com/moc2/token\";\n    }\n    String result = connect.get(url, params).trim();\n    return parseAccessTokenResult(result);\n  }\n\n  /**\n   * 权限自动续期，获取Access Token，此方法适用于PC网站。\n   * \n   * 从 {@link QQConnect}中获取 clientId, clientSecret\n   * \n   * @see OAuth2#refreshAccessToken(String, String, String, String, Boolean)\n   * @param refreshToken {@link AccessToken}中的refresToken。\n   */\n  public Result<AccessToken> refreshAccessToken(String refreshToken) {\n    return refreshAccessToken(connect.getClientId(), connect.getClientSecret(), refreshToken);\n  }\n\n  /**\n   * 权限自动续期，获取Access Token，此方法适用于PC网站。\n   * \n   * @see OAuth2#refreshAccessToken(String, String, String, String, Boolean)\n   * @param refreshToken {@link AccessToken}中的refresToken。\n   */\n  public Result<AccessToken> refreshAccessToken(String clientId, String clientSecret,\n      String refreshToken) {\n    return refreshAccessToken(clientId, clientSecret, \"refresh_token\", refreshToken, null);\n  }\n\n  /**\n   * 权限自动续期，获取Access Token\n   * \n   * 从 {@link QQConnect}中获取 clientId, clientSecret\n   * \n   * @see OAuth2#refreshAccessToken(String, String, String, String, Boolean)\n   * @param refreshToken {@link AccessToken}中的refresToken。\n   * @param wap 是否使wap版，默认为false\n   */\n  public Result<AccessToken> refreshAccessToken(String refreshToken, Boolean wap) {\n    return refreshAccessToken(connect.getClientId(), connect.getClientSecret(), refreshToken, wap);\n  }\n\n  /**\n   * 权限自动续期，获取Access Token\n   * \n   * @see OAuth2#refreshAccessToken(String, String, String, String, Boolean)\n   * @param refreshToken {@link AccessToken}中的refresToken。\n   * @param wap 是否使wap版，默认为false\n   */\n  public Result<AccessToken> refreshAccessToken(String clientId, String clientSecret,\n      String refreshToken, Boolean wap) {\n    return refreshAccessToken(clientId, clientSecret, \"refresh_token\", refreshToken, wap);\n  }\n\n  /**\n   * 权限自动续期，获取Access Token\n   * \n   * 文档地址：http://wiki.connect.qq.com/使用authorization_code获取access_token\n   * \n   * @param clientId 申请QQ登录成功后，分配给网站的appid。\n   * @param clientSecret 申请QQ登录成功后，分配给网站的appkey。\n   * @param grantType 授权类型，在本步骤中，此值为“refresh_token”。\n   * @param refresToken {@link AccessToken}中的refresToken。\n   * @param wap 是否使wap版，默认为false\n   */\n  public Result<AccessToken> refreshAccessToken(String clientId, String clientSecret,\n      String grantType, String refreshToken, Boolean wap) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"client_id\", clientId);\n    connect.addParameter(params, \"client_secret\", clientSecret);\n    connect.addParameter(params, \"grant_type\", grantType);\n    connect.addParameter(params, \"refresh_token\", refreshToken);\n    String url = \"https://graph.qq.com/oauth2.0/token\";\n    if (Boolean.TRUE.equals(wap)) {\n      url = \"https://graph.z.qq.com/moc2/token\";\n    }\n    String result = connect.get(url, params);\n    return parseAccessTokenResult(result);\n  }\n\n  /**\n   * 获取用户OpenID，此接口适用于PC网站访问\n   * \n   * 文档地址：http://wiki.connect.qq.com/获取用户openid_oauth2-0\n   * \n   * @param accessToken 授权令牌\n   */\n\n  public Result<OpenID> openId(String accessToken) {\n    return openId(accessToken, null);\n  }\n\n  /**\n   * 获取用户OpenID\n   * \n   * 文档地址：http://wiki.connect.qq.com/获取用户openid_oauth2-0\n   * \n   * @param accessToken 授权令牌\n   * @param wap 是否使wap网站访问\n   */\n  public Result<OpenID> openId(String accessToken, Boolean wap) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"access_token\", accessToken);\n    String url = \"https://graph.qq.com/oauth2.0/me\";\n    if (Boolean.TRUE.equals(wap)) {\n      url = \"https://graph.z.qq.com/moc2/me\";\n    }\n    String result = connect.get(url, params).trim();\n    JSONObject jsonObject;\n    if (result.startsWith(\"callback\")) {// PC网站的正确返回结果\n      jsonObject =\n          new JSONObject(result.substring(result.indexOf(\"{\"), result.lastIndexOf(\"}\") + 1));\n    } else {// WAP网站返回结果或错误信息\n      jsonObject = new JSONObject();\n      String[] results = result.split(\"\\\\&\");\n      for (String param : results) {\n        String[] keyValue = param.split(\"\\\\=\");\n        jsonObject.put(keyValue[0], keyValue.length > 0 ? keyValue[1] : null);\n      }\n      String errorCode = jsonObject.optString(\"code\", null);\n      if (errorCode != null) {\n        jsonObject.put(\"ret\", errorCode);// To match Error.parse()\n      }\n    }\n    return Result.parse(jsonObject, OpenID.class);\n  }\n\n  private Result<AccessToken> parseAccessTokenResult(String result) {\n    JSONObject jsonObject;\n    if (result.startsWith(\"callback\")) {// 错误信息\n                                        // callback({\"error\":100021,\"error_description\":\"get access token error\"});\n      jsonObject =\n          new JSONObject(result.substring(result.indexOf(\"{\"), result.lastIndexOf(\"}\") + 1));\n      return Result.parse(jsonObject, AccessToken.class);\n    } else {// 正确结果\n      jsonObject = new JSONObject();\n      String[] results = result.split(\"\\\\&\");\n      for (String param : results) {\n        String[] keyValue = param.split(\"\\\\=\");\n        jsonObject.put(keyValue[0], keyValue.length > 0 ? keyValue[1] : null);\n      }\n      String errorCode = jsonObject.optString(\"code\", null);\n      if (errorCode != null) {\n        jsonObject.put(\"ret\", errorCode);// To match Error.parse()\n      }\n    }\n    return Result.parse(jsonObject, AccessToken.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/api/QQConnect.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.mime.MultipartEntityBuilder;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.SDK;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.qq.connect.bean.NewT;\nimport com.belerweb.social.qq.connect.bean.TenpayAddress;\n\npublic final class QQConnect extends SDK {\n\n  private String clientId;\n  private String clientSecret;\n  private String redirectUri;\n\n  private OAuth2 oAuth2;\n  private User user;\n\n  public QQConnect(String clientId, String clientSecret) {\n    this.clientId = clientId;\n    this.clientSecret = clientSecret;\n  }\n\n  public QQConnect(String clientId, String clientSecret, String redirectUri) {\n    this(clientId, clientSecret);\n    this.redirectUri = redirectUri;\n  }\n\n  /**\n   * 收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要收听的用户的账户名列表。最多30个。\n   */\n  public Result<Error> addIdol(String accessToken, String openId, List<String> name) {\n    return addIdol(accessToken, openId, name, null);\n  }\n\n  /**\n   * 收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要收听的用户的账户名列表。最多30个。\n   * @param fopenids 要收听的用户的openid列表。最多30个。 name和fopenids至少选一个，若同时存在则以name值为主。\n   */\n  public Result<Error> addIdol(String accessToken, String openId, List<String> name,\n      List<String> fopenids) {\n    return addIdol(accessToken, getClientId(), openId, name, fopenids);\n  }\n\n  /**\n   * 收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要收听的用户的账户名列表。最多30个。\n   * @param fopenids 要收听的用户的openid列表。最多30个。 name和fopenids至少选一个，若同时存在则以name值为主。\n   */\n  public Result<Error> addIdol(String accessToken, String oauthConsumerKey, String openId,\n      List<String> name, List<String> fopenids) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    if (name != null && !name.isEmpty()) {\n      addParameter(params, \"name\", StringUtils.join(name, \",\"));\n    }\n    if (fopenids != null && !fopenids.isEmpty()) {\n      addParameter(params, \"fopenids\", StringUtils.join(fopenids, \"_\"));\n    }\n    addParameter(params, \"format\", \"json\");\n    return Result.parse(post(\"https://graph.qq.com/relation/add_idol\", params), Error.class);\n  }\n\n  /**\n   * 取消收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/del_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要取消收听的用户的账户名。 可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   */\n  public Result<Error> delIdol(String accessToken, String openId, String name) {\n    return delIdol(accessToken, openId, name, null);\n  }\n\n  /**\n   * 取消收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/del_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要取消收听的用户的账户名。 可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   * @param fopenid 要取消收听的用户的openid。可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   */\n  public Result<Error> delIdol(String accessToken, String openId, String name, String fopenid) {\n    return delIdol(accessToken, getClientId(), openId, name, fopenid);\n  }\n\n  /**\n   * 取消收听腾讯微博上的用户。\n   * \n   * 文档地址：http://wiki.connect.qq.com/del_idol\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param name 要取消收听的用户的账户名。 可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   * @param fopenid 要取消收听的用户的openid。可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   */\n  public Result<Error> delIdol(String accessToken, String oauthConsumerKey, String openId,\n      String name, String fopenid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addNotNullParameter(params, \"name\", name);\n    addNotNullParameter(params, \"fopenid\", fopenid);\n    addParameter(params, \"format\", \"json\");\n    return Result.parse(post(\"https://graph.qq.com/relation/del_idol\", params), Error.class);\n  }\n\n  /**\n   * 登录用户发表一篇新日志。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_one_blog\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param title 日志标题（纯文本，最大长度128个字节，utf-8编码）。\n   * @param content 文章内容（html数据，最大长度100*1024个字节，utf-8编码）.\n   */\n  public Result<Error> addOneBlog(String accessToken, String openId, String title, String content) {\n    return addOneBlog(accessToken, getClientId(), openId, title, content);\n  }\n\n  /**\n   * 登录用户发表一篇新日志。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_one_blog\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param title 日志标题（纯文本，最大长度128个字节，utf-8编码）。\n   * @param content 文章内容（html数据，最大长度100*1024个字节，utf-8编码）.\n   */\n  public Result<Error> addOneBlog(String accessToken, String oauthConsumerKey, String openId,\n      String title, String content) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addParameter(params, \"title\", title);\n    addParameter(params, \"content\", content);\n    addParameter(params, \"format\", \"json\");\n    return Result.parse(post(\"https://graph.qq.com/blog/add_one_blog\", params), Error.class);\n  }\n\n  /**\n   * 发表一条微博信息（纯文本）到腾讯微博平台上。注意连续两次发布的微博内容不可以重复。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   */\n  public Result<NewT> addT(String accessToken, String openId, String content) {\n    return addT(accessToken, getClientId(), openId, content);\n  }\n\n  /**\n   * 发表一条微博信息（纯文本）到腾讯微博平台上。注意连续两次发布的微博内容不可以重复。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   */\n  public Result<NewT> addT(String accessToken, String oauthConsumerKey, String openId,\n      String content) {\n    return addT(accessToken, oauthConsumerKey, openId, content, null, null, null, true, true);\n  }\n\n  /**\n   * 发表一条微博信息（纯文本）到腾讯微博平台上。注意连续两次发布的微博内容不可以重复。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   * @param clientIp 用户ip。必须正确填写用户侧真实ip，不能为内网ip及以127或255开头的ip，以分析用户所在地。\n   * @param lon 用户所在地理位置的经度。为实数，最多支持10位有效数字。有效范围：-180.0到+180.0，+表示东经，默认为0.0。\n   * @param lat 用户所在地理位置的纬度。为实数，最多支持10位有效数字。有效范围：-90.0到+90.0，+表示北纬，默认为0.0。\n   * @param sync 标识是否将发布的微博同步到QQ空间（0：同步； 1：不同步；），默认为0。该参数只支持OAuth1.0，OAuth2.0暂不支持。\n   * @param compatible 容错标志，支持按位操作，默认为0。0x20：微博内容长度超过140字则报错；0：以上错误均做容错处理，即发表普通微博。\n   */\n  public Result<NewT> addT(String accessToken, String oauthConsumerKey, String openId,\n      String content, String clientIp, Double lon, Double lat, Boolean sync, Boolean compatible) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addParameter(params, \"content\", content);\n    addNotNullParameter(params, \"clientip\", clientIp);\n    addNotNullParameter(params, \"lon\", lon);\n    addNotNullParameter(params, \"lat\", lat);\n    if (sync != null) {\n      addParameter(params, \"syncflag\", sync ? \"0\" : \"1\");\n    }\n    if (compatible != null) {\n      addParameter(params, \"compatibleflag\", compatible ? \"0\" : \"0x20\");\n    }\n    addParameter(params, \"format\", \"json\");\n    JSONObject jsonObject = new JSONObject(post(\"https://graph.qq.com/t/add_t\", params));\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<NewT>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), NewT.class);\n  }\n\n  /**\n   * 根据微博ID删除指定微博。\n   * \n   * 文档地址：http://wiki.connect.qq.com/del_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param id 微博消息的ID，用来唯一标识一条微博消息。\n   */\n  public Result<Error> delT(String accessToken, String openId, String id) {\n    return delT(accessToken, getClientId(), openId, id);\n  }\n\n  /**\n   * 上传一张图片，并发布一条消息到腾讯微博平台上。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_pic_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   * @param pic \n   *        要上传的图片的文件名以及图片的内容（在发送请求时，图片内容以二进制数据流的形式发送，见下面的请求示例）。图片仅支持gif、jpeg、jpg、png、bmp及ico格式（所有图片都会重新压缩\n   *        ，gif被重新压缩后不会再有动画效果），图片size小于4M。\n   */\n  public Result<NewT> addPicT(String accessToken, String openId, String content, byte[] pic) {\n    return addPicT(accessToken, getClientId(), openId, content, pic);\n  }\n\n  /**\n   * 上传一张图片，并发布一条消息到腾讯微博平台上。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_pic_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   * @param pic \n   *        要上传的图片的文件名以及图片的内容（在发送请求时，图片内容以二进制数据流的形式发送，见下面的请求示例）。图片仅支持gif、jpeg、jpg、png、bmp及ico格式（所有图片都会重新压缩\n   *        ，gif被重新压缩后不会再有动画效果），图片size小于4M。\n   */\n  public Result<NewT> addPicT(String accessToken, String oauthConsumerKey, String openId,\n      String content, byte[] pic) {\n    return addPicT(accessToken, oauthConsumerKey, openId, content, pic, null, null, null, true,\n        true);\n  }\n\n  /**\n   * 上传一张图片，并发布一条消息到腾讯微博平台上。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_pic_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param content\n   *        表示要发表的微博内容。必须为UTF-8编码，最长为140个汉字，也就是420字节。如果微博内容中有URL，后台会自动将该URL转换为短URL，每个URL折算成11个字节。\n   *        若在此处@好友，需正确填写好友的微博账号，而非昵称。\n   * @param pic \n   *        要上传的图片的文件名以及图片的内容（在发送请求时，图片内容以二进制数据流的形式发送，见下面的请求示例）。图片仅支持gif、jpeg、jpg、png、bmp及ico格式（所有图片都会重新压缩\n   *        ，gif被重新压缩后不会再有动画效果），图片size小于4M。\n   * @param clientIp 用户ip。必须正确填写用户侧真实ip，不能为内网ip及以127或255开头的ip，以分析用户所在地。\n   * @param lon 用户所在地理位置的经度。为实数，最多支持10位有效数字。有效范围：-180.0到+180.0，+表示东经，默认为0.0。\n   * @param lat 用户所在地理位置的纬度。为实数，最多支持10位有效数字。有效范围：-90.0到+90.0，+表示北纬，默认为0.0。\n   * @param sync 标识是否将发布的微博同步到QQ空间（0：同步； 1：不同步；），默认为0。该参数只支持OAuth1.0，OAuth2.0暂不支持。\n   * @param compatible 容错标志，支持按位操作，默认为0。0x20：微博内容长度超过140字则报错；0：以上错误均做容错处理，即发表普通微博。\n   */\n  public Result<NewT> addPicT(String accessToken, String oauthConsumerKey, String openId,\n      String content, byte[] pic, String clientIp, Double lon, Double lat, Boolean sync,\n      Boolean compatible) {\n    HttpPost request = new HttpPost(\"https://graph.qq.com/t/add_pic_t\");\n    MultipartEntityBuilder builder = MultipartEntityBuilder.create().addBinaryBody(\"pic\", pic);\n\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addParameter(params, \"content\", content);\n    addNotNullParameter(params, \"clientip\", clientIp);\n    addNotNullParameter(params, \"lon\", lon);\n    addNotNullParameter(params, \"lat\", lat);\n    if (sync != null) {\n      addParameter(params, \"syncflag\", sync ? \"0\" : \"1\");\n    }\n    if (compatible != null) {\n      addParameter(params, \"compatibleflag\", compatible ? \"0\" : \"0x20\");\n    }\n    addParameter(params, \"format\", \"json\");\n\n    for (NameValuePair nameValuePair : params) {\n      builder.addTextBody(nameValuePair.getName(), nameValuePair.getValue());\n    }\n    request.setEntity(builder.build());\n    try {\n      HttpResponse response = Http.CLIENT.execute(request);\n      String json = IOUtils.toString(response.getEntity().getContent());\n      JSONObject jsonObject = new JSONObject(json);\n      Error error = Error.parse(jsonObject);\n      if (error != null) {\n        return new Result<NewT>(error);\n      }\n      return Result.parse(jsonObject.getJSONObject(\"data\"), NewT.class);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    } finally {\n      request.releaseConnection();\n    }\n  }\n\n  /**\n   * 根据微博ID删除指定微博。\n   * \n   * 文档地址：http://wiki.connect.qq.com/del_t\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param id 微博消息的ID，用来唯一标识一条微博消息。\n   */\n  public Result<Error> delT(String accessToken, String oauthConsumerKey, String openId, String id) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addParameter(params, \"id\", id);\n    addParameter(params, \"format\", \"json\");\n    return Result.parse(post(\"https://graph.qq.com/t/del_t\", params), Error.class);\n  }\n\n  /**\n   * 获取财付通用户的收货地址。一个用户可能设置了多条收货地址信息。查询的用户必须为财付通用户，否则查询将返回失败。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_tenpay_addr\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   */\n  public Result<List<TenpayAddress>> getTenpayAddr(String accessToken, String openId) {\n    return getTenpayAddr(accessToken, getClientId(), openId, 0, Integer.MAX_VALUE);\n  }\n\n  /**\n   * 获取财付通用户的收货地址。一个用户可能设置了多条收货地址信息。查询的用户必须为财付通用户，否则查询将返回失败。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_tenpay_addr\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param offset\n   *        表示查询收货地址的偏移量，一般情况下offset可以不传值或传入0，表示从第一条开始读取。offset参数是为一种特殊情况准备的，即该收货人有很多条收获地址，需要分页展示，\n   *        则offset可设置为该页显示的条数。例如如果offset为10，则会跳过第10条收货地址，从第11条收货地址开始读取。\n   * @param limit 表示查询收货地址的返回限制数（即最多期望返回几个收货地址）。limit不传默认按照5来处理。\n   */\n  public Result<List<TenpayAddress>> getTenpayAddr(String accessToken, String openId,\n      Integer offset, Integer limit) {\n    return getTenpayAddr(accessToken, getClientId(), openId, offset, limit);\n  }\n\n  /**\n   * 获取财付通用户的收货地址。一个用户可能设置了多条收货地址信息。查询的用户必须为财付通用户，否则查询将返回失败。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_tenpay_addr\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oauthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openId 用户的ID，与QQ号码一一对应。\n   * @param offset\n   *        表示查询收货地址的偏移量，一般情况下offset可以不传值或传入0，表示从第一条开始读取。offset参数是为一种特殊情况准备的，即该收货人有很多条收获地址，需要分页展示，\n   *        则offset可设置为该页显示的条数。例如如果offset为10，则会跳过第10条收货地址，从第11条收货地址开始读取。\n   * @param limit 表示查询收货地址的返回限制数（即最多期望返回几个收货地址）。limit不传默认按照5来处理。\n   */\n  public Result<List<TenpayAddress>> getTenpayAddr(String accessToken, String oauthConsumerKey,\n      String openId, Integer offset, Integer limit) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    addParameter(params, \"access_token\", accessToken);\n    addParameter(params, \"oauth_consumer_key\", oauthConsumerKey);\n    addParameter(params, \"openid\", openId);\n    addParameter(params, \"offset\", offset == null ? \"0\" : offset);\n    addParameter(params, \"limit\", limit == null ? \"5\" : limit);\n    addParameter(params, \"ver\", \"1\");\n    addParameter(params, \"format\", \"json\");\n    JSONObject jsonObject =\n        new JSONObject(post(\"https://graph.qq.com/cft_info/get_tenpay_addr\", params));\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<List<TenpayAddress>>(error);\n    }\n    List<TenpayAddress> addresses = new ArrayList<TenpayAddress>();\n    Integer total = Result.parseInteger(jsonObject.get(\"ret_num\"));\n    for (int i = 0; i < total; i++) {\n      Integer index = Result.parseInteger(jsonObject.opt(\"Findex_\" + i));\n      if (index == null) {\n        break;\n      }\n      TenpayAddress address = new TenpayAddress();\n      address.setTotal(total);\n      address.setIndex(index);\n      address.setRegionId(Result.parseInteger(jsonObject.opt(\"FRegionId_\" + i)));\n      address.setStreet(jsonObject.optString(\"Faddrstreet_\" + i));\n      address.setZipcode(Result.toString(jsonObject.opt(\"Fzipcode_\" + i)));\n      address.setMobile(Result.toString(jsonObject.opt(\"Fmobile_\" + i)));\n      address.setTel(Result.toString(jsonObject.opt(\"Ftel_\" + i)));\n      address.setName(Result.toString(jsonObject.opt(\"Fname_\" + i)));\n      address.setCreated(Result.parseDate(jsonObject.opt(\"Fcreate_time_\" + i),\n          \"yyyy-MM-dd HH:mm:ss\", Locale.CHINA));\n      address.setModified(Result.parseDate(jsonObject.opt(\"Fmod_time_\" + i), \"yyyy-MM-dd HH:mm:ss\",\n          Locale.CHINA));\n      address.setLastUsed(Result.parseDate(jsonObject.opt(\"Flastuse_time_\" + i),\n          \"yyyy-MM-dd HH:mm:ss\", Locale.CHINA));\n      address.setUsedCount(Result.parseInteger(jsonObject.opt(\"FUsedCount_\" + i)));\n    }\n    return new Result<List<TenpayAddress>>(addresses);\n  }\n\n  public OAuth2 getOAuth2() {\n    if (oAuth2 == null) {\n      oAuth2 = new OAuth2(this);\n    }\n    return oAuth2;\n  }\n\n  public User getUser() {\n    if (user == null) {\n      user = new User(this);\n    }\n    return user;\n  }\n\n  public String getClientId() {\n    return clientId;\n  }\n\n  public void setClientId(String clientId) {\n    this.clientId = clientId;\n  }\n\n  public String getClientSecret() {\n    return clientSecret;\n  }\n\n  public void setClientSecret(String clientSecret) {\n    this.clientSecret = clientSecret;\n  }\n\n  public String getRedirectUri() {\n    return redirectUri;\n  }\n\n  public void setRedirectUri(String redirectUri) {\n    this.redirectUri = redirectUri;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/api/QZone.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.entity.mime.MultipartEntityBuilder;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.qq.connect.bean.Album;\nimport com.belerweb.social.qq.connect.bean.AlbumPrivilege;\nimport com.belerweb.social.qq.connect.bean.Photo;\nimport com.belerweb.social.qq.connect.bean.PicUploadResult;\n\n/**\n * QZone API，相册，日志...\n */\npublic final class QZone extends API {\n\n  protected QZone(QQConnect connect) {\n    super(connect);\n  }\n\n  /**\n   * 获取登录用户的相册列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/list_album\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<Album> listAlbum(String accessToken, String openid) {\n    return listAlbum(connect.getClientId(), accessToken, openid);\n  }\n\n  /**\n   * 获取登录用户的相册列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/list_album\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<Album> listAlbum(String oAuthConsumerKey, String accessToken, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://graph.qq.com/photo/list_album\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<Album>(error);\n    }\n    List<Album> results = new ArrayList<Album>();\n    JSONArray jsonArray = jsonObject.getJSONArray(\"album\");\n    for (int i = 0; i < jsonArray.length(); i++) {\n      results.add(Album.parse(jsonArray.getJSONObject(i)));\n    }\n    return new Result<Album>(results);\n  }\n\n  /**\n   * 登录用户创建一个公共相册。注：每个用户最多可创建10个相册。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_album\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param albumName 相册名，不能超过30个字符。\n   */\n  public Result<Album> addAlbum(String accessToken, String openid, String albumName) {\n    return addAlbum(connect.getClientId(), accessToken, openid, albumName, null, null, null, null);\n  }\n\n  /**\n   * 登录用户创建相册。注：每个用户最多可创建10个相册。\n   * \n   * 文档地址：http://wiki.connect.qq.com/add_album\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param albumName 相册名，不能超过30个字符。\n   * @param albumDesc 相册描述，不能超过200个字符。\n   * @param privilege 用户的ID，与QQ号码一一对应。 相册权限，\n   * \n   *        其取值含义为： 1=公开；3=只主人可见； 4=QQ好友可见； 5=问答加密。\n   * \n   *        不传则相册默认为公开权限。\n   * \n   *        如果priv取值为5，即相册是问答加密的，则必须包含问题和答案两个参数：\n   * \n   *        -question: 问题，不能超过30个字符。\n   * \n   *        -answer: 答案，不能超过30个字符。\n   */\n  public Result<Album> addAlbum(String oAuthConsumerKey, String accessToken, String openid,\n      String albumName, String albumDesc, AlbumPrivilege privilege, String question, String answer) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addParameter(params, \"albumname\", albumName);\n    connect.addNotNullParameter(params, \"albumdesc\", albumDesc);\n    connect.addNotNullParameter(params, \"priv\", privilege);\n    if (privilege == AlbumPrivilege.QUESTION) {\n      connect.addParameter(params, \"question\", question);\n      connect.addParameter(params, \"answer\", answer);\n    }\n    String json = connect.post(\"https://graph.qq.com/photo/add_album\", params);\n    return Result.parse(json, Album.class);\n  }\n\n  /**\n   * 登录用户上传照片，支持单张上传和批量上传。\n   * \n   * 文档地址：http://wiki.connect.qq.com/upload_pic\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param title 照片的命名，必须以.jpg, .gif, .png, .jpeg, .bmp此类后缀结尾。\n   * @param picture\n   * @param photoDesc 照片描述，注意照片描述不能超过200个字符。\n   * @param albumId 相册id。可不填，不填时则根据“mobile”标识选择默认上传的相册。\n   * @param mobile\n   *        标志位，0表示PC，1表示手机。用于当不传相册id时（即albumid为空时）控制是否传到手机相册。（1）如果传1，则当albumid为空时，图片会上传到手机相册；（2）\n   *        如果不传或传0，则当albumid为空时，图片会上传到贴图相册；\n   * @param lon 照片拍摄时的地理位置的经度。请使用原始数据（纯经纬度，0-360）。\n   * @param lat 照片拍摄时的地理位置的纬度。请使用原始数据（纯经纬度，0-360）。\n   * @param needFeed 标识上传照片时是否要发feed（0：不发feed； 1：发feed）。如果不填则默认为发feed。\n   * @param successNum 批量上传照片时，已成功上传的张数，指明上传完成情况。单张上传时可以不填，不填则默认为0。\n   * @param picNum\n   *        批量上传照片的总张数，如果不填则默认为1。如果picnum=1，为单张上传，发送单张上传feed；如果picnum>1，为批量上传，发送批量上传feed。批量上传方式：\n   *        picnum为一次上传照片的张数\n   *        ，successnum初始值为0，每调用一次照片上传接口后递增其值。信息中心中的feed表现形式：批量上传时最新的7张在feed中展示。其中最新上传的一张图片展示为大图\n   *        ，剩下的六张按从新到旧的顺序展示为小图，其他图片不在feed中展示。\n   */\n  public Result<PicUploadResult> uploadPic(String oAuthConsumerKey, String accessToken,\n      String openid, String title, byte[] picture, String photoDesc, String albumId,\n      Boolean mobile, Double lon, Double lat, Boolean needFeed, Integer successNum, Integer picNum) {\n    HttpPost request = new HttpPost(\"https://graph.qq.com/photo/upload_pic\");\n    MultipartEntityBuilder builder =\n        MultipartEntityBuilder.create().addBinaryBody(\"picture\", picture,\n            ContentType.create(\"image/\" + title.substring(title.lastIndexOf(\".\") + 1)), title);\n    builder.addTextBody(\"oauth_consumer_key\", oAuthConsumerKey);\n    builder.addTextBody(\"access_token\", accessToken);\n    builder.addTextBody(\"openid\", openid);\n    builder.addTextBody(\"format\", \"json\");\n    builder.addTextBody(\"title\", title);\n    if (photoDesc != null) {\n      builder.addTextBody(\"photodesc\", photoDesc);\n    }\n    if (albumId != null) {\n      builder.addTextBody(\"albumid\", albumId);\n    }\n    if (Boolean.TRUE.equals(mobile)) {\n      builder.addTextBody(\"mobile\", \"1\");\n    }\n    if (lon != null) {\n      builder.addTextBody(\"x\", lon.toString());\n    }\n    if (lat != null) {\n      builder.addTextBody(\"y\", lat.toString());\n    }\n    if (Boolean.FALSE.equals(needFeed)) {\n      builder.addTextBody(\"needfeed\", \"0\");\n    }\n    if (successNum != null) {\n      builder.addTextBody(\"successnum\", successNum.toString());\n    }\n    if (picNum != null) {\n      builder.addTextBody(\"picnum\", picNum.toString());\n    }\n    request.setEntity(builder.build());\n    try {\n      HttpResponse response = Http.CLIENT.execute(request);\n      String json = IOUtils.toString(response.getEntity().getContent());\n      return Result.parse(json, PicUploadResult.class);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    } finally {\n      request.releaseConnection();\n    }\n  }\n\n  /**\n   * 获取登录用户的照片列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/list_photo\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param albumId 表示要获取的照片列表所在的相册ID。\n   */\n  public Result<Photo> listPhoto(String accessToken, String openid, String albumId) {\n    return listPhoto(connect.getClientId(), accessToken, openid, albumId);\n  }\n\n  /**\n   * 获取登录用户的照片列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/list_photo\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param albumId 表示要获取的照片列表所在的相册ID。\n   */\n  public Result<Photo> listPhoto(String oAuthConsumerKey, String accessToken, String openid,\n      String albumId) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addParameter(params, \"albumid\", albumId);\n    String json = connect.get(\"https://graph.qq.com/photo/list_photo\", params);\n\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<Photo>(error);\n    }\n    List<Photo> results = new ArrayList<Photo>();\n    JSONArray jsonArray = jsonObject.getJSONArray(\"photos\");\n    for (int i = 0; i < jsonArray.length(); i++) {\n      results.add(Photo.parse(jsonArray.getJSONObject(i)));\n    }\n    return new Result<Photo>(results);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/api/User.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.http.NameValuePair;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 访问用户资料\n */\npublic final class User extends API {\n\n  protected User(QQConnect connect) {\n    super(connect);\n  }\n\n  /**\n   * 获取登录用户在QQ空间的信息，包括昵称、头像、性别及黄钻信息（包括黄钻等级、是否年费黄钻等）。此接口仅支持网站调用\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_user_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   *        可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getUserInfo(String accessToken,\n      String openid) {\n    return getUserInfo(accessToken, connect.getClientId(), openid);\n  }\n\n  /**\n   * 获取登录用户在QQ空间的信息，包括昵称、头像、性别及黄钻信息（包括黄钻等级、是否年费黄钻等）。此接口仅支持网站调用\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_user_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openid 用户的ID，与QQ号码一一对应。\n   *        可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getUserInfo(String accessToken,\n      String oAuthConsumerKey, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addNotNullParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://graph.qq.com/user/get_user_info\", params);\n    return Result.parse(json, com.belerweb.social.qq.connect.bean.User.class);\n  }\n\n  /**\n   * 获取移动端应用的登录用户在QQ空间的简单个人信息，包括昵称、头像和黄钻信息（包括黄钻等级、是否年费黄钻等），以及用户的QQ头像。 此接口仅支持移动端应用调用\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_simple_userinfo\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openid 用户的ID，与QQ号码一一对应。\n   *        可通过调用https://graph.qq.com/oauth2.0/me?access_token=YOUR_ACCESS_TOKEN 来获取。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getSimpleUserInfo(String accessToken,\n      String oAuthConsumerKey, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addNotNullParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://openmobile.qq.com/user/get_simple_userinfo\", params);\n    return Result.parse(json, com.belerweb.social.qq.connect.bean.User.class);\n  }\n\n  /**\n   * 获取已登录用户的关于QQ会员业务的基本资料。\n   * \n   * 基本资料包括以下信息：是否为“普通包月会员”，是否为“年费会员”，QQ会员等级信息，是否为“豪华版QQ会员”，是否为“钻皇会员”，是否为“SVIP”。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_vip_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getVipInfo(String accessToken,\n      String openid) {\n    return getVipInfo(accessToken, connect.getClientId(), openid);\n  }\n\n  /**\n   * 获取已登录用户的关于QQ会员业务的基本资料。\n   * \n   * 基本资料包括以下信息：是否为“普通包月会员”，是否为“年费会员”，QQ会员等级信息，是否为“豪华版QQ会员”，是否为“钻皇会员”，是否为“SVIP”。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_vip_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getVipInfo(String accessToken,\n      String oAuthConsumerKey, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://graph.qq.com/user/get_vip_info\", params);\n    return Result.parse(json, com.belerweb.social.qq.connect.bean.User.class);\n  }\n\n  /**\n   * 获取已登录用户的关于QQ会员业务的详细资料。\n   * \n   * 详细资料包括：用户会员的历史属性，用户会员特权的到期时间，用户最后一次充值会员业务的支付渠道，用户开通会员的主要驱动因素。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_vip_rich_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getVipRichInfo(String accessToken,\n      String openid) {\n    return getVipRichInfo(connect.getClientId(), accessToken, openid);\n  }\n\n  /**\n   * 获取已登录用户的关于QQ会员业务的详细资料。\n   * \n   * 详细资料包括：用户会员的历史属性，用户会员特权的到期时间，用户最后一次充值会员业务的支付渠道，用户开通会员的主要驱动因素。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_vip_rich_info\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<com.belerweb.social.qq.connect.bean.User> getVipRichInfo(String oAuthConsumerKey,\n      String accessToken, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://graph.qq.com/user/get_vip_rich_info\", params);\n    return Result.parse(json, com.belerweb.social.qq.connect.bean.User.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/api/Weibo.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.http.NameValuePair;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.qq.connect.bean.FanList;\nimport com.belerweb.social.qq.connect.bean.IdolList;\nimport com.belerweb.social.qq.connect.bean.RepostList;\nimport com.belerweb.social.qq.connect.bean.WeiboUser;\n\n/**\n * 腾讯微博API\n */\npublic final class Weibo extends API {\n\n  protected Weibo(QQConnect connect) {\n    super(connect);\n  }\n\n  /**\n   * 获取腾讯微博登录用户的用户资料。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<WeiboUser> getInfo(String accessToken, String openid) {\n    return getInfo(connect.getClientId(), accessToken, openid);\n  }\n\n  /**\n   * 获取腾讯微博登录用户的用户资料。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_info\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   */\n  public Result<WeiboUser> getInfo(String oAuthConsumerKey, String accessToken, String openid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    String json = connect.get(\"https://graph.qq.com/user/get_info\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<WeiboUser>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), WeiboUser.class);\n  }\n\n  /**\n   * 获取腾讯微博其他用户详细信息。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_other_info\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param name 其他用户的账户名。可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   */\n  public Result<WeiboUser> getOtherInfo(String accessToken, String openid, String name) {\n    return getOtherInfo(connect.getClientId(), accessToken, openid, name, null);\n  }\n\n  /**\n   * 获取腾讯微博其他用户详细信息。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_other_info\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param name 其他用户的账户名。可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   * @param fopenid 其他用户的账户名。可选，name和fopenid至少选一个，若同时存在则以name值为主。\n   */\n  public Result<WeiboUser> getOtherInfo(String oAuthConsumerKey, String accessToken, String openid,\n      String name, String fopenid) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addNotNullParameter(params, \"name\", name);\n    connect.addNotNullParameter(params, \"fopenid\", fopenid);\n    String json = connect.get(\"https://graph.qq.com/user/get_other_info\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<WeiboUser>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), WeiboUser.class);\n  }\n\n  /**\n   * 获取登录用户的听众列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_fanslist\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param reqNum 必须。请求获取的听众个数。取值范围为1-30。\n   * @param startIndex 必须。请求获取听众列表的起始位置。第一页：0；继续向下翻页：reqnum*（page-1）。\n   */\n  public Result<FanList> getFansList(String accessToken, String openid, int reqNum, int startIndex) {\n    return getFansList(connect.getClientId(), accessToken, openid, reqNum, startIndex, null, null,\n        null);\n  }\n\n  /**\n   * 获取登录用户的听众列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_fanslist\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param reqNum 必须。请求获取的听众个数。取值范围为1-30。\n   * @param startIndex 必须。请求获取听众列表的起始位置。第一页：0；继续向下翻页：reqnum*（page-1）。\n   * @param newMode \n   *        获取听众信息的模式。获取听众信息的模式，默认值为0。0：旧模式，新添加的听众信息排在前面，最多只能拉取1000个听众的信息。1：新模式，可以拉取所有听众的信息，暂时不支持排序。\n   * @param install 判断获取的是安装应用的听众，还是未安装应用的听众。 0：不考虑该参数；1：获取已安装应用的听众信息；2：获取未安装应用的听众信息。\n   * @param sex 按性别过滤标识，默认为0。此参数当mode=0时使用，支持排序。 1：获取的是男性听众信息；2：获取的是女性听众信息；0：不进行性别过滤，获取所有听众信息。\n   */\n  public Result<FanList> getFansList(String oAuthConsumerKey, String accessToken, String openid,\n      int reqNum, int startIndex, Boolean newMode, Integer install, Integer sex) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addParameter(params, \"reqnum\", reqNum);\n    connect.addParameter(params, \"startindex\", startIndex);\n    if (Boolean.TRUE.equals(newMode)) {\n      connect.addParameter(params, \"mode\", \"1\");\n    }\n    connect.addNotNullParameter(params, \"install\", install);\n    connect.addNotNullParameter(params, \"sex\", sex);\n    String json = connect.get(\"https://graph.qq.com/relation/get_fanslist\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<FanList>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), FanList.class);\n  }\n\n  /**\n   * 获取登录用户收听的人的列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_idollist\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param reqNum 必须。请求获取的听众个数。取值范围为1-30。\n   * @param startIndex 必须。请求获取听众列表的起始位置。第一页：0；继续向下翻页：reqnum*（page-1）。\n   */\n  public Result<IdolList> getIdolList(String accessToken, String openid, int reqNum, int startIndex) {\n    return getIdolList(connect.getClientId(), accessToken, openid, reqNum, startIndex, null, null);\n  }\n\n  /**\n   * 获取登录用户收听的人的列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_idollist\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param reqNum 必须。请求获取的听众个数。取值范围为1-30。\n   * @param startIndex 必须。请求获取听众列表的起始位置。第一页：0；继续向下翻页：reqnum*（page-1）。\n   * @param newMode 获取收听的人的信息模式，默认为0。\n   *        0：旧模式，新添加的收听的人信息排在前面，最多只能拉取1000个收听的人的信息。1：新模式，最多可拉取10000个收听的人的信息，暂不支持排序。\n   * @param install 判断获取的是安装了应用的收听好友，还是未安装应用的收听好友。0：不考虑该参数；1：获取已安装应用的收听好友信息；2：获取未安装应用的收听好友信息。\n   */\n  public Result<IdolList> getIdolList(String oAuthConsumerKey, String accessToken, String openid,\n      int reqNum, int startIndex, Boolean newMode, Integer install) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addParameter(params, \"reqnum\", reqNum);\n    connect.addParameter(params, \"startindex\", startIndex);\n    if (Boolean.TRUE.equals(newMode)) {\n      connect.addParameter(params, \"mode\", \"1\");\n    }\n    connect.addNotNullParameter(params, \"install\", install);\n    String json = connect.get(\"https://graph.qq.com/relation/get_idollist\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<IdolList>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), IdolList.class);\n  }\n\n  /**\n   * 获取一条微博的转播或评论信息列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_repost_list\n   * \n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param reqNum 必须。请求获取的听众个数。取值范围为1-30。\n   * @param startIndex 必须。请求获取听众列表的起始位置。第一页：0；继续向下翻页：reqnum*（page-1）。\n   */\n  public Result<RepostList> getRepostList(String accessToken, String openid, int flag,\n      String rootId, int pageFlag, int pageTime, int reqNum, String twitterId) {\n    return getRepostList(connect.getClientId(), accessToken, openid, flag, rootId, pageFlag,\n        pageTime, reqNum, twitterId);\n  }\n\n  /**\n   * 获取一条微博的转播或评论信息列表。\n   * \n   * 文档地址：http://wiki.connect.qq.com/get_repost_list\n   * \n   * @param oAuthConsumerKey 申请QQ登录成功后，分配给应用的appid\n   * @param accessToken 可通过使用Authorization_Code获取Access_Token 或来获取。access_token有3个月有效期。\n   * @param openid 用户的ID，与QQ号码一一对应。\n   * @param flag 标识获取的是转播列表还是点评列表。0：获取转播列表；1：获取点评列表；2：转播列表和点评列表都获取。\n   * @param rootId 转发或点评的源微博的ID。\n   * @param pageFlag 分页标识。0：第一页；1：向下翻页；2：向上翻页。\n   * @param pageTime 分页标识。0：第一页；1：向下翻页；2：向上翻页。\n   * @param reqNum 每次请求记录的条数。取值为1-100条。\n   * @param twitterId 翻页时使用。第1-100条：0；继续向下翻页：上一次请求返回的最后一条记录id。\n   */\n  public Result<RepostList> getRepostList(String oAuthConsumerKey, String accessToken,\n      String openid, int flag, String rootId, int pageFlag, int pageTime, int reqNum,\n      String twitterId) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    connect.addParameter(params, \"oauth_consumer_key\", oAuthConsumerKey);\n    connect.addParameter(params, \"access_token\", accessToken);\n    connect.addParameter(params, \"openid\", openid);\n    connect.addParameter(params, \"format\", \"json\");\n    connect.addParameter(params, \"flag\", flag);\n    connect.addParameter(params, \"rootid\", rootId);\n    connect.addParameter(params, \"pageflag\", pageFlag);\n    connect.addParameter(params, \"pagetime\", pageTime);\n    connect.addParameter(params, \"reqnum\", reqNum);\n    connect.addParameter(params, \"twitterid\", twitterId);\n    String json = connect.get(\"https://graph.qq.com/t/get_repost_list\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<RepostList>(error);\n    }\n    return Result.parse(jsonObject.getJSONObject(\"data\"), RepostList.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/AccessToken.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class AccessToken extends JsonBean {\n\n  public AccessToken() {}\n\n  private AccessToken(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String token;// 授权令牌，Access_Token。\n  private Long expiresIn;// 该access token的有效期，单位为秒。\n  private String refreshToken;// 在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。\n\n  /**\n   * 授权令牌，Access_Token。\n   */\n  public String getToken() {\n    return token;\n  }\n\n  public void setToken(String token) {\n    this.token = token;\n  }\n\n\n  /**\n   * 该access token的有效期，单位为秒。\n   */\n  public Long getExpiresIn() {\n    return expiresIn;\n  }\n\n  public void setExpiresIn(Long expiresIn) {\n    this.expiresIn = expiresIn;\n  }\n\n  /**\n   * 在授权自动续期步骤中，获取新的Access_Token时需要提供的参数。\n   */\n  public String getRefreshToken() {\n    return refreshToken;\n  }\n\n  public void setRefreshToken(String refreshToken) {\n    this.refreshToken = refreshToken;\n  }\n\n  public static AccessToken parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    AccessToken obj = new AccessToken(jsonObject);\n    obj.token = jsonObject.getString(\"access_token\");\n    obj.expiresIn = Result.parseLong(jsonObject.opt(\"expires_in\"));\n    obj.refreshToken = Result.toString(jsonObject.opt(\"refresh_token\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Album.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Album extends JsonBean {\n\n  public Album() {}\n\n  private Album(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String albumId;\n  private String classId;\n  private Date createTime;\n  private String name;\n  private String description;\n  private String cover;\n  private Integer picNum;\n\n  public String getAlbumId() {\n    return albumId;\n  }\n\n  public void setAlbumId(String albumId) {\n    this.albumId = albumId;\n  }\n\n  public String getClassId() {\n    return classId;\n  }\n\n  public void setClassId(String classId) {\n    this.classId = classId;\n  }\n\n  public Date getCreateTime() {\n    return createTime;\n  }\n\n  public void setCreateTime(Date createTime) {\n    this.createTime = createTime;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public String getCover() {\n    return cover;\n  }\n\n  public void setCover(String cover) {\n    this.cover = cover;\n  }\n\n  public Integer getPicNum() {\n    return picNum;\n  }\n\n  public void setPicNum(Integer picNum) {\n    this.picNum = picNum;\n  }\n\n  public static Album parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Album obj = new Album(jsonObject);\n    obj.albumId = Result.toString(jsonObject.get(\"albumid\"));\n    obj.classId = Result.toString(jsonObject.opt(\"classid\"));\n    obj.createTime = Result.parseTimeSeconds(jsonObject.opt(\"createtime\"));\n    obj.name = Result.toString(jsonObject.opt(\"name\"));\n    obj.description = Result.toString(jsonObject.opt(\"desc\"));\n    obj.cover = Result.toString(jsonObject.opt(\"coverurl\"));\n    obj.picNum = Result.parseInteger(jsonObject.opt(\"picnum\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/AlbumPrivilege.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\npublic enum AlbumPrivilege {\n  PUBLIC(1), PRIVATE(3), FRIEND(4), QUESTION(5);\n\n  private Integer value;\n\n  private AlbumPrivilege(Integer value) {\n    this.value = value;\n  }\n\n  public Integer value() {\n    return value;\n  }\n\n  @Override\n  public String toString() {\n    return value.toString();\n  }\n\n  public static AlbumPrivilege parse(Integer value) {\n    if (value == null) {\n      return null;\n    }\n    if (value == 1) {\n      return PUBLIC;\n    }\n    if (value == 3) {\n      return PRIVATE;\n    }\n    if (value == 4) {\n      return FRIEND;\n    }\n    if (value == 5) {\n      return QUESTION;\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Company.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Company extends JsonBean {\n\n  public Company() {}\n\n  private Company(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 公司id。\n  private String companyName;// 公司名称。\n  private String departmentName;// 部门名称。\n  private Integer beginYear;// 开始年。\n  private Integer endYear;// 结束年。\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getCompanyName() {\n    return companyName;\n  }\n\n  public void setCompanyName(String companyName) {\n    this.companyName = companyName;\n  }\n\n  public String getDepartmentName() {\n    return departmentName;\n  }\n\n  public void setDepartmentName(String departmentName) {\n    this.departmentName = departmentName;\n  }\n\n  public Integer getBeginYear() {\n    return beginYear;\n  }\n\n  public void setBeginYear(Integer beginYear) {\n    this.beginYear = beginYear;\n  }\n\n  public Integer getEndYear() {\n    return endYear;\n  }\n\n  public void setEndYear(Integer endYear) {\n    this.endYear = endYear;\n  }\n\n  public static Company parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Company obj = new Company(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.companyName = Result.toString(jsonObject.opt(\"company_name\"));\n    obj.departmentName = Result.toString(jsonObject.opt(\"department_name\"));\n    obj.beginYear = Result.parseInteger(jsonObject.opt(\"begin_year\"));\n    obj.endYear = Result.parseInteger(jsonObject.opt(\"end_year\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Display.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\npublic enum Display {\n\n  DEFAULT,\n\n  MOBILE;\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Education.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Education extends JsonBean {\n\n  public Education() {}\n\n  private Education(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 教育信息记录ID。\n  private Integer year;// 入学年。\n  private String schoolId;// 学校ID。学校ID与学校具体信息的对应关系请参见教育信息数据库。\n  private String departmentId;// 院系ID。院系ID与院系具体信息的对应关系请参见教育信息数据库。\n  private Integer level;// 学历级别。\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public Integer getYear() {\n    return year;\n  }\n\n  public void setYear(Integer year) {\n    this.year = year;\n  }\n\n  public String getSchoolId() {\n    return schoolId;\n  }\n\n  public void setSchoolId(String schoolId) {\n    this.schoolId = schoolId;\n  }\n\n  public String getDepartmentId() {\n    return departmentId;\n  }\n\n  public void setDepartmentId(String departmentId) {\n    this.departmentId = departmentId;\n  }\n\n  public Integer getLevel() {\n    return level;\n  }\n\n  public void setLevel(Integer level) {\n    this.level = level;\n  }\n\n  public static Education parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Education obj = new Education(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.schoolId = Result.toString(jsonObject.opt(\"company_name\"));\n    obj.departmentId = Result.toString(jsonObject.opt(\"department_name\"));\n    obj.year = Result.parseInteger(jsonObject.opt(\"begin_year\"));\n    obj.level = Result.parseInteger(jsonObject.opt(\"end_year\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/FanList.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class FanList extends JsonBean {\n\n  public FanList() {}\n\n  private FanList(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Date timestamp;\n  private Boolean hasNext;\n  private List<WeiboUser> fans;\n\n  public Date getTimestamp() {\n    return timestamp;\n  }\n\n  public void setTimestamp(Date timestamp) {\n    this.timestamp = timestamp;\n  }\n\n  public Boolean getHasNext() {\n    return hasNext;\n  }\n\n  public void setHasNext(Boolean hasNext) {\n    this.hasNext = hasNext;\n  }\n\n  public List<WeiboUser> getFans() {\n    return fans;\n  }\n\n  public void setFans(List<WeiboUser> fans) {\n    this.fans = fans;\n  }\n\n  public static FanList parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    FanList obj = new FanList(jsonObject);\n    obj.timestamp = Result.parseTimeSeconds(jsonObject.get(\"timestamp\"));\n    obj.hasNext = Result.parseBoolean(jsonObject.get(\"hasnext\"));\n    obj.fans = Result.parse(jsonObject.optJSONArray(\"info\"), WeiboUser.class);\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Gut.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\n/**\n * QQ登录页面版本\n */\npublic enum Gut {\n\n  WML(1),\n\n  XHTML(2);\n\n  private int value;\n\n  private Gut(int value) {\n    this.value = value;\n  }\n\n  public int value() {\n    return value;\n  }\n\n  @Override\n  public String toString() {\n    return String.valueOf(value);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/IdolList.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class IdolList extends JsonBean {\n\n  public IdolList() {}\n\n  private IdolList(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Date timestamp;\n  private Boolean hasNext;\n  private List<WeiboUser> fans;\n\n  public Date getTimestamp() {\n    return timestamp;\n  }\n\n  public void setTimestamp(Date timestamp) {\n    this.timestamp = timestamp;\n  }\n\n  public Boolean getHasNext() {\n    return hasNext;\n  }\n\n  public void setHasNext(Boolean hasNext) {\n    this.hasNext = hasNext;\n  }\n\n  public List<WeiboUser> getFans() {\n    return fans;\n  }\n\n  public void setFans(List<WeiboUser> fans) {\n    this.fans = fans;\n  }\n\n  public static IdolList parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    IdolList obj = new IdolList(jsonObject);\n    obj.timestamp = Result.parseTimeSeconds(jsonObject.get(\"timestamp\"));\n    obj.hasNext = Result.parseBoolean(jsonObject.get(\"hasnext\"));\n    obj.fans = Result.parse(jsonObject.optJSONArray(\"info\"), WeiboUser.class);\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Image.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Image extends JsonBean {\n\n  public Image() {}\n\n  private Image(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String url;\n  private Integer width;\n  private Integer height;\n\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  public Integer getWidth() {\n    return width;\n  }\n\n  public void setWidth(Integer width) {\n    this.width = width;\n  }\n\n  public Integer getHeight() {\n    return height;\n  }\n\n  public void setHeight(Integer height) {\n    this.height = height;\n  }\n\n  public static Image parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Image obj = new Image(jsonObject);\n    obj.url = Result.toString(jsonObject.opt(\"url\"));\n    obj.width = Result.parseInteger(jsonObject.opt(\"width\"));\n    obj.height = Result.parseInteger(jsonObject.opt(\"height\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Music.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Music extends JsonBean {\n\n  public Music() {}\n\n  private Music(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String author;// 演唱者。\n  private String url;// 音频地址。\n  private String title;// 音频名字，歌名。\n\n\n  public String getAuthor() {\n    return author;\n  }\n\n  public void setAuthor(String author) {\n    this.author = author;\n  }\n\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n  }\n\n  public static Music parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Music obj = new Music(jsonObject);\n    obj.author = Result.toString(jsonObject.opt(\"author\"));\n    obj.url = Result.toString(jsonObject.opt(\"url\"));\n    obj.title = Result.toString(jsonObject.opt(\"title\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/NewT.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 新发布的微博的返回信息\n */\npublic class NewT extends JsonBean {\n\n  public NewT() {}\n\n  private NewT(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Long id;// 微博的ID，用来唯一标识一条微博。\n  private Date time;// 微博的发表时间。\n  private String imgUrl;// 图片分享后的url地址。\n\n  public Long getId() {\n    return id;\n  }\n\n  public void setId(Long id) {\n    this.id = id;\n  }\n\n  public Date getTime() {\n    return time;\n  }\n\n  public void setTime(Date time) {\n    this.time = time;\n  }\n\n  public String getImgUrl() {\n    return imgUrl;\n  }\n\n  public void setImgUrl(String imgUrl) {\n    this.imgUrl = imgUrl;\n  }\n\n  public static NewT parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    NewT obj = new NewT(jsonObject);\n    obj.id = Result.parseLong(jsonObject.get(\"id\"));\n    obj.time = new Date(Result.parseLong(jsonObject.get(\"time\")) * 1000);\n    obj.imgUrl = jsonObject.optString(\"imgurl\");\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/OpenID.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\n\npublic class OpenID extends JsonBean {\n\n  public OpenID() {}\n\n  private OpenID(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String clientId;\n  private String openId; // openid是此网站上唯一对应用户身份的标识，网站可将此ID进行存储便于用户下次登录时辨识其身份，或将其与用户在网站上的原有账号进行绑定。\n\n  /**\n   * openid是此网站上唯一对应用户身份的标识，网站可将此ID进行存储便于用户下次登录时辨识其身份，或将其与用户在网站上的原有账号进行绑定。\n   */\n  public String getClientId() {\n    return clientId;\n  }\n\n  public void setClientId(String clientId) {\n    this.clientId = clientId;\n  }\n\n  public String getOpenId() {\n    return openId;\n  }\n\n  public void setOpenId(String openId) {\n    this.openId = openId;\n  }\n\n  public static OpenID parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    OpenID obj = new OpenID(jsonObject);\n    obj.clientId = jsonObject.getString(\"client_id\");\n    obj.openId = jsonObject.getString(\"openid\");\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Photo.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\nimport java.util.Locale;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Photo extends JsonBean {\n\n  public Photo() {}\n\n  private Photo(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String sLoc;\n  private String lLoc;\n  private String name;\n  private String description;\n  private Date updatedTime;\n  private Date uploadedTime;\n  private String smallUrl;\n  private Image largeImage;\n\n  public String getsLoc() {\n    return sLoc;\n  }\n\n  public void setsLoc(String sLoc) {\n    this.sLoc = sLoc;\n  }\n\n  public String getlLoc() {\n    return lLoc;\n  }\n\n  public void setlLoc(String lLoc) {\n    this.lLoc = lLoc;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public Date getUpdatedTime() {\n    return updatedTime;\n  }\n\n  public void setUpdatedTime(Date updatedTime) {\n    this.updatedTime = updatedTime;\n  }\n\n  public Date getUploadedTime() {\n    return uploadedTime;\n  }\n\n  public void setUploadedTime(Date uploadedTime) {\n    this.uploadedTime = uploadedTime;\n  }\n\n  public String getSmallUrl() {\n    return smallUrl;\n  }\n\n  public void setSmallUrl(String smallUrl) {\n    this.smallUrl = smallUrl;\n  }\n\n  public Image getLargeImage() {\n    return largeImage;\n  }\n\n  public void setLargeImage(Image largeImage) {\n    this.largeImage = largeImage;\n  }\n\n  public static Photo parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Photo obj = new Photo(jsonObject);\n    obj.sLoc = Result.toString(jsonObject.opt(\"sloc\"));\n    obj.lLoc = Result.toString(jsonObject.opt(\"lloc\"));\n    obj.name = Result.toString(jsonObject.opt(\"name\"));\n    obj.description = Result.toString(jsonObject.opt(\"desc\"));\n    obj.updatedTime = Result.parseTimeSeconds(jsonObject.opt(\"updated_time\"));\n    obj.uploadedTime =\n        Result.parseDate(jsonObject.opt(\"uploaded_time\"), \"yyyy-MM-dd HH:mm:ss\", Locale.CHINA);\n    obj.smallUrl = Result.toString(jsonObject.opt(\"small_url\"));\n    obj.largeImage = Image.parse(jsonObject.optJSONObject(\"large_image\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/PicUploadResult.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class PicUploadResult extends JsonBean {\n\n  public PicUploadResult() {}\n\n  private PicUploadResult(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String albumId;\n  private String lLoc;\n  private String sLoc;\n  private String largeUrl;\n  private String smallUrl;\n  private Integer width;\n  private Integer height;\n\n  public String getAlbumId() {\n    return albumId;\n  }\n\n  public void setAlbumId(String albumId) {\n    this.albumId = albumId;\n  }\n\n  public String getlLoc() {\n    return lLoc;\n  }\n\n  public void setlLoc(String lLoc) {\n    this.lLoc = lLoc;\n  }\n\n  public String getsLoc() {\n    return sLoc;\n  }\n\n  public void setsLoc(String sLoc) {\n    this.sLoc = sLoc;\n  }\n\n  public String getLargeUrl() {\n    return largeUrl;\n  }\n\n  public void setLargeUrl(String largeUrl) {\n    this.largeUrl = largeUrl;\n  }\n\n  public String getSmallUrl() {\n    return smallUrl;\n  }\n\n  public void setSmallUrl(String smallUrl) {\n    this.smallUrl = smallUrl;\n  }\n\n  public Integer getWidth() {\n    return width;\n  }\n\n  public void setWidth(Integer width) {\n    this.width = width;\n  }\n\n  public Integer getHeight() {\n    return height;\n  }\n\n  public void setHeight(Integer height) {\n    this.height = height;\n  }\n\n  public static PicUploadResult parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    PicUploadResult obj = new PicUploadResult(jsonObject);\n    obj.albumId = Result.toString(jsonObject.opt(\"albumid\"));\n    obj.lLoc = Result.toString(jsonObject.opt(\"lloc\"));\n    obj.sLoc = Result.toString(jsonObject.opt(\"sloc\"));\n    obj.largeUrl = Result.toString(jsonObject.opt(\"large_url\"));\n    obj.smallUrl = Result.toString(jsonObject.opt(\"small_url\"));\n    obj.width = Result.parseInteger(jsonObject.opt(\"width\"));\n    obj.height = Result.parseInteger(jsonObject.opt(\"height\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/RepostList.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class RepostList extends JsonBean {\n\n  public RepostList() {}\n\n  private RepostList(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Date timestamp;\n  private Boolean hasNext;\n  private Integer totalNum;\n  private List<TweetInfo> tweets;\n  private Map<String, String> nameNickMap = new HashMap<String, String>();\n\n  public Date getTimestamp() {\n    return timestamp;\n  }\n\n  public void setTimestamp(Date timestamp) {\n    this.timestamp = timestamp;\n  }\n\n  public Boolean getHasNext() {\n    return hasNext;\n  }\n\n  public void setHasNext(Boolean hasNext) {\n    this.hasNext = hasNext;\n  }\n\n  public Integer getTotalNum() {\n    return totalNum;\n  }\n\n  public void setTotalNum(Integer totalNum) {\n    this.totalNum = totalNum;\n  }\n\n  public List<TweetInfo> getTweets() {\n    return tweets;\n  }\n\n  public void setTweets(List<TweetInfo> tweets) {\n    this.tweets = tweets;\n  }\n\n  public static RepostList parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    RepostList obj = new RepostList(jsonObject);\n    obj.timestamp = Result.parseTimeSeconds(jsonObject.get(\"timestamp\"));\n    obj.hasNext = Result.parseBoolean(jsonObject.get(\"hasnext\"));\n    obj.totalNum = Result.parseInteger(jsonObject.get(\"totalnum\"));\n    obj.tweets = Result.parse(jsonObject.optJSONArray(\"info\"), TweetInfo.class);\n    JSONObject map = jsonObject.optJSONObject(\"user\");\n    if (map != null) {\n      Iterator<?> keys = map.keys();\n      while (keys.hasNext()) {\n        String key = keys.next().toString();\n        obj.nameNickMap.put(key, map.get(key).toString());\n      }\n    }\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Scope.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\n/**\n * 请求用户授权时向用户显示的可进行授权的列表。\n */\npublic enum Scope {\n\n  /**\n   * do_like\n   */\n  DO_LIKE(\"do_like\"),\n\n  /**\n   * 获取登录用户的昵称、头像、性别\n   */\n  GET_USER_INFO(\"get_user_info\"),\n\n  /**\n   * 获取登录用户的昵称、头像、性别\n   */\n  GET_SIMPLE_USERINFO(\"get_simple_userinfo\"),\n\n  /**\n   * 获取QQ会员的基本信息\n   */\n  GET_VIP_INFO(\"get_vip_info\"),\n\n  /**\n   * 获取QQ会员的高级信息\n   */\n  GET_VIP_RICH_INFO(\"get_vip_rich_info\"),\n\n  /**\n   * 发表日志到QQ空间\n   */\n  ADD_ONE_BLOG(\"add_one_blog\"),\n\n  /**\n   * 获取用户QQ空间相册列表\n   */\n  LIST_ALBUM(\"list_album\"),\n\n  /**\n   * 上传一张照片到QQ空间相册\n   */\n  UPLOAD_PIC(\"upload_pic\"),\n\n  /**\n   * 在用户的空间相册里，创建一个新的个人相册\n   */\n  ADD_ALBUM(\"add_album\"),\n\n  /**\n   * 获取用户QQ空间相册中的照片列表\n   */\n  LIST_PHOTO(\"list_photo\"),\n\n  /**\n   * 获取登录用户在腾讯微博详细资料\n   */\n  GET_INFO(\"get_info\"),\n\n  /**\n   * 发表一条微博\n   */\n  ADD_T(\"add_t\"),\n\n  /**\n   * 删除一条微博\n   */\n  DEL_T(\"del_t\"),\n\n  /**\n   * 发表一条带图片的微博\n   */\n  ADD_PIC_T(\"add_pic_t\"),\n\n  /**\n   * 获取单条微博的转发或点评列表\n   */\n  GET_REPOST_LIST(\"get_repost_list\"),\n\n  /**\n   * 获取他人微博资料\n   */\n  GET_OTHER_INFO(\"get_other_info\"),\n\n  /**\n   * 我的微博粉丝列表\n   */\n  GET_FANSLIST(\"get_fanslist\"),\n\n  /**\n   * 我的微博偶像列表\n   */\n  GET_IDOLLIST(\"get_idollist\"),\n\n  /**\n   * 收听某个微博用户\n   */\n  ADD_IDOL(\"add_idol\"),\n\n  /**\n   * 取消收听某个微博用户\n   */\n  DEL_IDOL(\"del_idol\"),\n\n  /**\n   * 在这个网站上将展现您财付通登记的收货地址\n   */\n  GET_TENPAY_ADDR(\"get_tenpay_addr\");\n\n  private String scope;\n\n  private Scope(String scope) {\n    this.scope = scope;\n  }\n\n  public String value() {\n    return scope;\n  }\n\n  @Override\n  public String toString() {\n    return scope;\n  }\n\n  public static final Scope[] ALL = new Scope[] {Scope.DO_LIKE, Scope.GET_USER_INFO,\n      Scope.GET_SIMPLE_USERINFO, Scope.GET_VIP_INFO, Scope.GET_VIP_RICH_INFO, Scope.ADD_ONE_BLOG,\n      Scope.LIST_ALBUM, Scope.UPLOAD_PIC, Scope.ADD_ALBUM, Scope.LIST_PHOTO, Scope.GET_INFO,\n      Scope.ADD_T, Scope.DEL_T, Scope.ADD_PIC_T, Scope.GET_REPOST_LIST, Scope.GET_OTHER_INFO,\n      Scope.GET_FANSLIST, Scope.GET_IDOLLIST, Scope.ADD_IDOL, Scope.DEL_IDOL,\n      Scope.GET_TENPAY_ADDR,};\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/SendPrivate.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\npublic enum SendPrivate {\n  IDOL(0), FRIEND(1), PUBLIC(2);\n\n  private Integer value;\n\n  private SendPrivate(Integer value) {\n    this.value = value;\n  }\n\n  public Integer value() {\n    return value;\n  }\n\n  @Override\n  public String toString() {\n    return value.toString();\n  }\n\n  public static SendPrivate parse(Integer value) {\n    if (value == null) {\n      return null;\n    }\n    if (value == 0) {\n      return IDOL;\n    }\n    if (value == 1) {\n      return FRIEND;\n    }\n    if (value == 2) {\n      return PUBLIC;\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Tag.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Tag extends JsonBean {\n\n  public Tag() {}\n\n  private Tag(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 个人标签id。\n  private String name;// 标签名。\n\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public static Tag parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Tag obj = new Tag(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.name = Result.toString(jsonObject.opt(\"name\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/TenpayAddress.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\n\n/**\n * 财付通收货地址\n */\npublic class TenpayAddress {\n\n  private Integer index;// 收货信息的索引编号\n  private Integer regionId;// 收货信息的地区编号\n  private String street;// 收货信息的收货地址\n  private String zipcode;// 收货信息的邮编\n  private String mobile;// 收货信息的收货人移动电话\n  private String tel;// 收货信息的收货人固定电话\n  private String name;// 收货信息的收货人姓名\n  private Date created;// 收货信息的创建时间\n  private Date modified;// 收货信息上一次被修改的时间\n  private Date lastUsed;// 收货信息上一次被使用的时间\n  private Integer usedCount;// 收货信息被使用过的次数\n  private Integer total;// 该用户财付通收货地址的个数\n\n  public Integer getIndex() {\n    return index;\n  }\n\n  public void setIndex(Integer index) {\n    this.index = index;\n  }\n\n  public Integer getRegionId() {\n    return regionId;\n  }\n\n  public void setRegionId(Integer regionId) {\n    this.regionId = regionId;\n  }\n\n  public String getStreet() {\n    return street;\n  }\n\n  public void setStreet(String street) {\n    this.street = street;\n  }\n\n  public String getZipcode() {\n    return zipcode;\n  }\n\n  public void setZipcode(String zipcode) {\n    this.zipcode = zipcode;\n  }\n\n  public String getMobile() {\n    return mobile;\n  }\n\n  public void setMobile(String mobile) {\n    this.mobile = mobile;\n  }\n\n  public String getTel() {\n    return tel;\n  }\n\n  public void setTel(String tel) {\n    this.tel = tel;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public Date getCreated() {\n    return created;\n  }\n\n  public void setCreated(Date created) {\n    this.created = created;\n  }\n\n  public Date getModified() {\n    return modified;\n  }\n\n  public void setModified(Date modified) {\n    this.modified = modified;\n  }\n\n  public Date getLastUsed() {\n    return lastUsed;\n  }\n\n  public void setLastUsed(Date lastUsed) {\n    this.lastUsed = lastUsed;\n  }\n\n  public Integer getUsedCount() {\n    return usedCount;\n  }\n\n  public void setUsedCount(Integer usedCount) {\n    this.usedCount = usedCount;\n  }\n\n  public Integer getTotal() {\n    return total;\n  }\n\n  public void setTotal(Integer total) {\n    this.total = total;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/TweetInfo.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class TweetInfo extends JsonBean {\n\n  public TweetInfo() {}\n\n  private TweetInfo(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 微博唯一id。\n  private String countryCode;// 发表微博时所作的国家代码。\n  private String provinceCode;// 发表微博时所作的省份代码。\n  private String cityCode;// 发表微博时所作的城市代码。\n  private String emotionType;// 心情类型。\n  private String emotionUrl;// 心情图片url。\n  private String from;// 来源。\n  private String fromUrl;// 来源url。\n  private String image;// 图片url。\n  private String geo;// 地理位置信息。\n  private String location;// 发表者所在地。\n  private Double longitude;// 经度。\n  private Double latitude;// 纬度。\n  private Music music;\n  private String origText;// 微博原始内容。\n  private Boolean self;// 是否自已发的的微博\n  private Integer status;// 表示微博的状态。0：正常；1：微博被系统删除；2：审核中；3；微博被用户删除；4：源微博被系统审核删除。\n  private String text;// 微博内容。\n  private Date timestamp;// 服务器时间戳，不能用于翻页。\n  private Integer type;// 表示微博的类型。1：原创发表；2：转播；3：私信；4：回复；5：没有内容的回复；6：提及；7：评论。\n  private Video video;// 视频信息。\n  private Integer count;// 转播次数。\n  private Integer mCount;// 评论数。\n  private Boolean isVip;// 用户是否为微博认证用户\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getCountryCode() {\n    return countryCode;\n  }\n\n  public void setCountryCode(String countryCode) {\n    this.countryCode = countryCode;\n  }\n\n  public String getProvinceCode() {\n    return provinceCode;\n  }\n\n  public void setProvinceCode(String provinceCode) {\n    this.provinceCode = provinceCode;\n  }\n\n  public String getCityCode() {\n    return cityCode;\n  }\n\n  public void setCityCode(String cityCode) {\n    this.cityCode = cityCode;\n  }\n\n  public String getEmotionType() {\n    return emotionType;\n  }\n\n  public void setEmotionType(String emotionType) {\n    this.emotionType = emotionType;\n  }\n\n  public String getEmotionUrl() {\n    return emotionUrl;\n  }\n\n  public void setEmotionUrl(String emotionUrl) {\n    this.emotionUrl = emotionUrl;\n  }\n\n  public String getFrom() {\n    return from;\n  }\n\n  public void setFrom(String from) {\n    this.from = from;\n  }\n\n  public String getFromUrl() {\n    return fromUrl;\n  }\n\n  public void setFromUrl(String fromUrl) {\n    this.fromUrl = fromUrl;\n  }\n\n  public String getImage() {\n    return image;\n  }\n\n  public void setImage(String image) {\n    this.image = image;\n  }\n\n  public String getGeo() {\n    return geo;\n  }\n\n  public void setGeo(String geo) {\n    this.geo = geo;\n  }\n\n  public String getLocation() {\n    return location;\n  }\n\n  public void setLocation(String location) {\n    this.location = location;\n  }\n\n  public Double getLongitude() {\n    return longitude;\n  }\n\n  public void setLongitude(Double longitude) {\n    this.longitude = longitude;\n  }\n\n  public Double getLatitude() {\n    return latitude;\n  }\n\n  public void setLatitude(Double latitude) {\n    this.latitude = latitude;\n  }\n\n  public Music getMusic() {\n    return music;\n  }\n\n  public void setMusic(Music music) {\n    this.music = music;\n  }\n\n  public String getOrigText() {\n    return origText;\n  }\n\n  public void setOrigText(String origText) {\n    this.origText = origText;\n  }\n\n  public Boolean getSelf() {\n    return self;\n  }\n\n  public void setSelf(Boolean self) {\n    this.self = self;\n  }\n\n  public Integer getStatus() {\n    return status;\n  }\n\n  public void setStatus(Integer status) {\n    this.status = status;\n  }\n\n  public String getText() {\n    return text;\n  }\n\n  public void setText(String text) {\n    this.text = text;\n  }\n\n  public Date getTimestamp() {\n    return timestamp;\n  }\n\n  public void setTimestamp(Date timestamp) {\n    this.timestamp = timestamp;\n  }\n\n  public Integer getType() {\n    return type;\n  }\n\n  public void setType(Integer type) {\n    this.type = type;\n  }\n\n  public Video getVideo() {\n    return video;\n  }\n\n  public void setVideo(Video video) {\n    this.video = video;\n  }\n\n  public Integer getCount() {\n    return count;\n  }\n\n  public void setCount(Integer count) {\n    this.count = count;\n  }\n\n  public Integer getmCount() {\n    return mCount;\n  }\n\n  public void setmCount(Integer mCount) {\n    this.mCount = mCount;\n  }\n\n  public Boolean getIsVip() {\n    return isVip;\n  }\n\n  public void setIsVip(Boolean isVip) {\n    this.isVip = isVip;\n  }\n\n  public static TweetInfo parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    TweetInfo obj = new TweetInfo(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.countryCode = Result.toString(jsonObject.opt(\"country_code\"));\n    obj.provinceCode = Result.toString(jsonObject.opt(\"province_code\"));\n    obj.cityCode = Result.toString(jsonObject.opt(\"city_code\"));\n    obj.emotionType = Result.toString(jsonObject.opt(\"emotiontype\"));\n    obj.emotionUrl = Result.toString(jsonObject.opt(\"emotionurl\"));\n    obj.from = Result.toString(jsonObject.opt(\"from\"));\n    obj.fromUrl = Result.toString(jsonObject.opt(\"fromurl\"));\n    obj.image = Result.toString(jsonObject.opt(\"image\"));\n    obj.geo = Result.toString(jsonObject.opt(\"geo\"));\n    obj.location = Result.toString(jsonObject.opt(\"location\"));\n    obj.longitude = Result.parseDouble(jsonObject.opt(\"longitude\"));\n    obj.latitude = Result.parseDouble(jsonObject.opt(\"latitude\"));\n    obj.music = Music.parse(jsonObject.optJSONObject(\"music\"));\n    obj.origText = Result.toString(jsonObject.opt(\"origtext\"));\n    obj.self = Result.parseBoolean(jsonObject.opt(\"self\"));\n    obj.status = Result.parseInteger(jsonObject.opt(\"status\"));\n    obj.text = Result.toString(jsonObject.opt(\"text\"));\n    obj.timestamp = Result.parseTimeSeconds(jsonObject.opt(\"timestamp\"));\n    obj.type = Result.parseInteger(jsonObject.opt(\"type\"));\n    obj.video = Video.parse(jsonObject.optJSONObject(\"video\"));\n    obj.count = Result.parseInteger(jsonObject.opt(\"count\"));\n    obj.mCount = Result.parseInteger(jsonObject.opt(\"mcount\"));\n    obj.isVip = Result.parseBoolean(jsonObject.opt(\"isvip\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/User.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.Date;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Gender;\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class User extends JsonBean {\n\n  public User() {}\n\n  private User(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String nickname;// 用户在QQ空间的昵称。\n  private String figureUrl;// 大小为30×30像素的QQ空间头像URL。\n  private String figureUrl1;// 大小为50×50像素的QQ空间头像URL。\n  private String figureUrl2;// 大小为100×100像素的QQ空间头像URL。\n  private String figureUrlQQ1;// 大小为40×40像素的QQ头像URL。 40x40像素则是一定会有。\n  private String figureUrlQQ2;// 大小为100×100像素的QQ头像URL。需要注意，不是所有的用户都拥有QQ的100x100的头像。\n  private Gender gender;// 性别。 如果获取不到则默认返回\"男\"\n  private Boolean isYellowVip;// 标识用户是否为黄钻用户\n  private Boolean vip;// 标识用户是否为黄钻用户\n  private Integer yellowVipLevel;// 黄钻等级\n  private Integer level;// 黄钻等级\n  private Boolean isYellowYearVip;// 标识是否为年费黄钻用户\n  private Boolean isQQVip;// 标识是否QQ会员\n  private Integer qqVipLevel;// QQ会员等级\n  private Boolean isQQYearVip;// 标识是否为年费QQ会员\n  private Boolean isLost;\n  private Date qqVipStart;// QQ会员最后一次充值时间\n  private Date qqVipEnd;// QQ会员期限\n  private Integer qqVipPayway;// QQ会员充值方式\n  private Date qqYearVipStart;// QQ年费会员最后一次充值时间\n  private Date qqYearVipEnd;// QQ年费会员期限\n  private Integer qqYearVipPayway;// QQ年费会员充值方式\n  private Date qqZuanhuangStart;// QQ钻皇最后一次充值时间\n  private Date qqZuanhuangEnd;// QQ钻皇期限\n  private Integer qqZuanhuangPayway;// QQ钻皇充值方式\n  private Date qqHaohuaStart;// 豪华版QQ会员最后一次充值时间\n  private Date qqHaohuaEnd;// 豪华版QQ会员期限\n  private Integer qqHaohuaPayway;// 豪华版QQ会员充值方式\n  private Date qqSvipStart;// QQ SVIP最后一次充值时间，预留字段，当前信息无效\n  private Date qqSvipEnd;// QQ SVIP期限，预留字段，当前信息无效\n  private Integer qqSvipPayway;// QQ SVIP充值方式，预留字段，当前信息无效\n  private Date historyPayTime;// 非会员历史充值时间，仅在用户是非会员时信息有效\n  private Date historyEndTime;// 非会员历史充值到期时间，仅在用户是非会员时信息有效\n\n  /**\n   * 用户在QQ空间的昵称。\n   */\n  public String getNickname() {\n    return nickname;\n  }\n\n  public void setNickname(String nickname) {\n    this.nickname = nickname;\n  }\n\n  /**\n   * 大小为30×30像素的QQ空间头像URL。\n   */\n  public String getFigureUrl() {\n    return figureUrl;\n  }\n\n  public void setFigureUrl(String figureUrl) {\n    this.figureUrl = figureUrl;\n  }\n\n  /**\n   * 大小为50×50像素的QQ空间头像URL。\n   */\n  public String getFigureUrl1() {\n    return figureUrl1;\n  }\n\n  public void setFigureUrl1(String figureUrl1) {\n    this.figureUrl1 = figureUrl1;\n  }\n\n  /**\n   * 大小为100×100像素的QQ空间头像URL。\n   */\n  public String getFigureUrl2() {\n    return figureUrl2;\n  }\n\n  public void setFigureUrl2(String figureUrl2) {\n    this.figureUrl2 = figureUrl2;\n  }\n\n  /**\n   * 大小为40×40像素的QQ头像URL。 40x40像素则是一定会有。\n   */\n  public String getFigureUrlQQ1() {\n    return figureUrlQQ1;\n  }\n\n  public void setFigureUrlQQ1(String figureUrlQQ1) {\n    this.figureUrlQQ1 = figureUrlQQ1;\n  }\n\n  /**\n   * 大小为100×100像素的QQ头像URL。需要注意，不是所有的用户都拥有QQ的100x100的头像。\n   */\n  public String getFigureUrlQQ2() {\n    return figureUrlQQ2;\n  }\n\n  public void setFigureUrlQQ2(String figureUrlQQ2) {\n    this.figureUrlQQ2 = figureUrlQQ2;\n  }\n\n  /**\n   * 性别。 如果获取不到则默认返回\"男\"\n   */\n  public Gender getGender() {\n    return gender;\n  }\n\n  public void setGender(Gender gender) {\n    this.gender = gender;\n  }\n\n  /**\n   * 标识用户是否为黄钻用户\n   */\n  public Boolean getIsYellowVip() {\n    return isYellowVip;\n  }\n\n  public void setIsYellowVip(Boolean isYellowVip) {\n    this.isYellowVip = isYellowVip;\n  }\n\n  /**\n   * 标识用户是否为黄钻用户\n   */\n  public Boolean getVip() {\n    return vip;\n  }\n\n  public void setVip(Boolean vip) {\n    this.vip = vip;\n  }\n\n  /**\n   * 黄钻等级\n   */\n  public Integer getYellowVipLevel() {\n    return yellowVipLevel;\n  }\n\n  public void setYellowVipLevel(Integer yellowVipLevel) {\n    this.yellowVipLevel = yellowVipLevel;\n  }\n\n  /**\n   * 黄钻等级\n   */\n  public Integer getLevel() {\n    return level;\n  }\n\n  public void setLevel(Integer level) {\n    this.level = level;\n  }\n\n  /**\n   * 标识是否为年费黄钻用户\n   */\n  public Boolean getIsYellowYearVip() {\n    return isYellowYearVip;\n  }\n\n  public void setIsYellowYearVip(Boolean isYellowYearVip) {\n    this.isYellowYearVip = isYellowYearVip;\n  }\n\n  /**\n   * 标识是否QQ会员\n   */\n  public Boolean getIsQQVip() {\n    return isQQVip;\n  }\n\n  public void setIsQQVip(Boolean isQQVip) {\n    this.isQQVip = isQQVip;\n  }\n\n  /**\n   * QQ会员等级\n   */\n  public Integer getQqVipLevel() {\n    return qqVipLevel;\n  }\n\n  public void setQqVipLevel(Integer qqVipLevel) {\n    this.qqVipLevel = qqVipLevel;\n  }\n\n  /**\n   * 是否是QQ年费会员\n   */\n  public Boolean getIsQQYearVip() {\n    return isQQYearVip;\n  }\n\n  public void setIsQQYearVip(Boolean isQQYearVip) {\n    this.isQQYearVip = isQQYearVip;\n  }\n\n  public Boolean getIsLost() {\n    return isLost;\n  }\n\n  public void setIsLost(Boolean isLost) {\n    this.isLost = isLost;\n  }\n\n  /**\n   * QQ会员最后一次充值时间\n   */\n  public Date getQqVipStart() {\n    return qqVipStart;\n  }\n\n  public void setQqVipStart(Date qqVipStart) {\n    this.qqVipStart = qqVipStart;\n  }\n\n  /**\n   * QQ会员期限\n   */\n  public Date getQqVipEnd() {\n    return qqVipEnd;\n  }\n\n  public void setQqVipEnd(Date qqVipEnd) {\n    this.qqVipEnd = qqVipEnd;\n  }\n\n  /**\n   * QQ会员充值方式\n   */\n  public Integer getQqVipPayway() {\n    return qqVipPayway;\n  }\n\n  public void setQqVipPayway(Integer qqVipPayway) {\n    this.qqVipPayway = qqVipPayway;\n  }\n\n  /**\n   * QQ年费会员最后一次充值时间\n   */\n  public Date getQqYearVipStart() {\n    return qqYearVipStart;\n  }\n\n  public void setQqYearVipStart(Date qqYearVipStart) {\n    this.qqYearVipStart = qqYearVipStart;\n  }\n\n  /**\n   * QQ年费会员期限\n   */\n  public Date getQqYearVipEnd() {\n    return qqYearVipEnd;\n  }\n\n  public void setQqYearVipEnd(Date qqYearVipEnd) {\n    this.qqYearVipEnd = qqYearVipEnd;\n  }\n\n  /**\n   * QQ年费会员充值方式\n   */\n  public Integer getQqYearVipPayway() {\n    return qqYearVipPayway;\n  }\n\n  public void setQqYearVipPayway(Integer qqYearVipPayway) {\n    this.qqYearVipPayway = qqYearVipPayway;\n  }\n\n  /**\n   * QQ钻皇最后一次充值时间\n   */\n  public Date getQqZuanhuangStart() {\n    return qqZuanhuangStart;\n  }\n\n  public void setQqZuanhuangStart(Date qqZuanhuangStart) {\n    this.qqZuanhuangStart = qqZuanhuangStart;\n  }\n\n  /**\n   * QQ钻皇期限\n   */\n  public Date getQqZuanhuangEnd() {\n    return qqZuanhuangEnd;\n  }\n\n  public void setQqZuanhuangEnd(Date qqZuanhuangEnd) {\n    this.qqZuanhuangEnd = qqZuanhuangEnd;\n  }\n\n  /**\n   * QQ钻皇充值方式\n   */\n  public Integer getQqZuanhuangPayway() {\n    return qqZuanhuangPayway;\n  }\n\n  public void setQqZuanhuangPayway(Integer qqZuanhuangPayway) {\n    this.qqZuanhuangPayway = qqZuanhuangPayway;\n  }\n\n  /**\n   * 豪华版QQ会员最后一次充值时间\n   */\n  public Date getQqHaohuaStart() {\n    return qqHaohuaStart;\n  }\n\n  public void setQqHaohuaStart(Date qqHaohuaStart) {\n    this.qqHaohuaStart = qqHaohuaStart;\n  }\n\n  /**\n   * 豪华版QQ会员期限\n   */\n  public Date getQqHaohuaEnd() {\n    return qqHaohuaEnd;\n  }\n\n  public void setQqHaohuaEnd(Date qqHaohuaEnd) {\n    this.qqHaohuaEnd = qqHaohuaEnd;\n  }\n\n  /**\n   * 豪华版QQ会员充值方式\n   */\n  public Integer getQqHaohuaPayway() {\n    return qqHaohuaPayway;\n  }\n\n  public void setQqHaohuaPayway(Integer qqHaohuaPayway) {\n    this.qqHaohuaPayway = qqHaohuaPayway;\n  }\n\n  /**\n   * QQ SVIP最后一次充值时间，预留字段，当前信息无效\n   */\n  public Date getQqSvipStart() {\n    return qqSvipStart;\n  }\n\n  public void setQqSvipStart(Date qqSvipStart) {\n    this.qqSvipStart = qqSvipStart;\n  }\n\n  /**\n   * QQ SVIP期限，预留字段，当前信息无效\n   */\n  public Date getQqSvipEnd() {\n    return qqSvipEnd;\n  }\n\n  public void setQqSvipEnd(Date qqSvipEnd) {\n    this.qqSvipEnd = qqSvipEnd;\n  }\n\n  /**\n   * QQ SVIP充值方式，预留字段，当前信息无效\n   */\n  public Integer getQqSvipPayway() {\n    return qqSvipPayway;\n  }\n\n  public void setQqSvipPayway(Integer qqSvipPayway) {\n    this.qqSvipPayway = qqSvipPayway;\n  }\n\n  /**\n   * 非会员历史充值时间，仅在用户是非会员时信息有效\n   */\n  public Date getHistoryPayTime() {\n    return historyPayTime;\n  }\n\n  public void setHistoryPayTime(Date historyPayTime) {\n    this.historyPayTime = historyPayTime;\n  }\n\n  /**\n   * 非会员历史充值到期时间，仅在用户是非会员时信息有效\n   */\n  public Date getHistoryEndTime() {\n    return historyEndTime;\n  }\n\n  public void setHistoryEndTime(Date historyEndTime) {\n    this.historyEndTime = historyEndTime;\n  }\n\n  public static User parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    User obj = new User(jsonObject);\n    obj.nickname = Result.toString(jsonObject.opt(\"nickname\"));\n    obj.figureUrl = Result.toString(jsonObject.opt(\"figureurl\"));\n    obj.figureUrl1 = Result.toString(jsonObject.opt(\"figureurl_1\"));\n    obj.figureUrl2 = Result.toString(jsonObject.opt(\"figureurl_2\"));\n    obj.figureUrlQQ1 = Result.toString(jsonObject.opt(\"figureurl_qq_1\"));\n    obj.figureUrlQQ2 = Result.toString(jsonObject.opt(\"figureurl_qq_2\"));\n    obj.gender = Gender.parse(jsonObject.optString(\"gender\", null));\n    obj.isYellowVip = Result.parseBoolean(jsonObject.opt(\"is_yellow_vip\"));\n    obj.vip = Result.parseBoolean(jsonObject.opt(\"vip\"));\n    obj.yellowVipLevel = Result.parseInteger(jsonObject.opt(\"yellow_vip_level\"));\n    obj.level = Result.parseInteger(jsonObject.opt(\"level\"));\n    obj.isYellowYearVip = Result.parseBoolean(jsonObject.opt(\"is_yellow_year_vip\"));\n    obj.isQQVip = Result.parseBoolean(jsonObject.opt(\"is_qq_vip\"));\n    obj.qqVipLevel = Result.parseInteger(jsonObject.opt(\"qq_vip_level\"));\n    obj.isQQYearVip = Result.parseBoolean(jsonObject.opt(\"is_qq_year_vip\"));\n    obj.isLost = Result.parseBoolean(jsonObject.opt(\"is_lost\"));\n    obj.qqVipStart = Result.parseTimeSeconds(jsonObject.opt(\"qq_vip_start\"));\n    obj.qqVipEnd = Result.parseTimeSeconds(jsonObject.opt(\"qq_vip_end\"));\n    obj.qqVipPayway = Result.parseInteger(jsonObject.opt(\"qq_vip_payway\"));\n    obj.qqYearVipStart = Result.parseTimeSeconds(jsonObject.opt(\"qq_year_vip_start\"));\n    obj.qqYearVipEnd = Result.parseTimeSeconds(jsonObject.opt(\"qq_year_vip_end\"));\n    obj.qqYearVipPayway = Result.parseInteger(jsonObject.opt(\"qq_year_vip_payway\"));\n    obj.qqZuanhuangStart = Result.parseTimeSeconds(jsonObject.opt(\"qq_zuanhuang_start\"));\n    obj.qqZuanhuangEnd = Result.parseTimeSeconds(jsonObject.opt(\"qq_zuanhuang_end\"));\n    obj.qqZuanhuangPayway = Result.parseInteger(jsonObject.opt(\"qq_zuanhuang_payway\"));\n    obj.qqHaohuaStart = Result.parseTimeSeconds(jsonObject.opt(\"qq_haohua_start\"));\n    obj.qqHaohuaEnd = Result.parseTimeSeconds(jsonObject.opt(\"qq_haohua_end\"));\n    obj.qqHaohuaPayway = Result.parseInteger(jsonObject.opt(\"qq_haohua_payway\"));\n    obj.qqSvipStart = Result.parseTimeSeconds(jsonObject.opt(\"qq_svip_start\"));\n    obj.qqSvipEnd = Result.parseTimeSeconds(jsonObject.opt(\"qq_svip_end\"));\n    obj.qqSvipPayway = Result.parseInteger(jsonObject.opt(\"qq_svip_payway\"));\n    obj.historyPayTime = Result.parseTimeSeconds(jsonObject.opt(\"history_pay_time\"));\n    obj.historyEndTime = Result.parseTimeSeconds(jsonObject.opt(\"history_end_time\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/Video.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class Video extends JsonBean {\n\n  public Video() {}\n\n  private Video(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String picUrl;// 缩略图。\n  private String player;// 播放器地址。\n  private String realUrl;// 视频原地址。\n  private String shortUrl;// 视频的短url。\n  private String title;// 视频标题。\n\n  public String getPicUrl() {\n    return picUrl;\n  }\n\n  public void setPicUrl(String picUrl) {\n    this.picUrl = picUrl;\n  }\n\n  public String getPlayer() {\n    return player;\n  }\n\n  public void setPlayer(String player) {\n    this.player = player;\n  }\n\n  public String getRealUrl() {\n    return realUrl;\n  }\n\n  public void setRealUrl(String realUrl) {\n    this.realUrl = realUrl;\n  }\n\n  public String getShortUrl() {\n    return shortUrl;\n  }\n\n  public void setShortUrl(String shortUrl) {\n    this.shortUrl = shortUrl;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n  }\n\n  public static Video parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Video obj = new Video(jsonObject);\n    obj.picUrl = Result.toString(jsonObject.opt(\"picurl\"));\n    obj.player = Result.toString(jsonObject.opt(\"player\"));\n    obj.realUrl = Result.toString(jsonObject.opt(\"realurl\"));\n    obj.shortUrl = Result.toString(jsonObject.opt(\"shorturl\"));\n    obj.title = Result.toString(jsonObject.opt(\"title\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/connect/bean/WeiboUser.java",
    "content": "package com.belerweb.social.qq.connect.bean;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Gender;\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class WeiboUser extends JsonBean {\n\n  public WeiboUser() {}\n\n  private WeiboUser(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String openId;// 登录用户的唯一ID，与name一一对应。\n  private String name;// 登录用户的帐号名\n  private String nick;// 登录用户昵称。\n  private Date regTime;// 登录注册时间。\n  private Date birth;\n  private String countryCode;\n  private String provinceCode;\n  private String cityCode;\n  private List<Company> companies;\n  private List<Education> educations;\n  private Integer fansNum;// 登录用户听众数。\n  private Integer favNum;// 登录用户收藏数。\n  private String head;\n  private String homeCountryCode;// 登录用户家乡所在国家代码。\n  private String homeProvinceCode;// 登录用户家乡所在省份代码。\n  private String homeCityCode;// 登录用户家乡所在城市代码。\n  private String hometownCode;// 登录用户家乡所在城镇代码。\n  private String homepage;// 个人主页。\n  private Integer idolNum;// 登录用户收听的人数。\n  private String industryCode;// 行业ID。\n  private String introduction;// 登录用户的个人介绍。\n  private Boolean isEnt;// 登录用户是否为企业机构\n  private Boolean isMyBlack;// 是否在当前用户的黑名单中\n  private Boolean isMyFans;// 是否是当前用户的听众\n  private Boolean isMyIdol;// 是否是当前用户的偶像\n  private Boolean isRealName;// 登录用户是否实名认证\n  private Boolean isVip;// 登录用户是否为微博认证用户\n  private String location;// 登录用户所在地。\n  private Integer mutualFansNum;// 登录用户的互听好友数\n  private SendPrivate sendPrivate;// 是否允许所有人给当前用户发私信。\n  private Gender gender;\n  private List<Tag> tags;\n  private List<TweetInfo> tweets;\n  private Integer tweetNum;\n  private String verifyInfo;\n  private Integer exp;\n  private Integer level;\n  private String seqId;\n\n  public String getOpenId() {\n    return openId;\n  }\n\n  public void setOpenId(String openId) {\n    this.openId = openId;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getNick() {\n    return nick;\n  }\n\n  public void setNick(String nick) {\n    this.nick = nick;\n  }\n\n  public Date getRegTime() {\n    return regTime;\n  }\n\n  public void setRegTime(Date regTime) {\n    this.regTime = regTime;\n  }\n\n  public Date getBirth() {\n    return birth;\n  }\n\n  public void setBirth(Date birth) {\n    this.birth = birth;\n  }\n\n  public String getCountryCode() {\n    return countryCode;\n  }\n\n  public void setCountryCode(String countryCode) {\n    this.countryCode = countryCode;\n  }\n\n  public String getProvinceCode() {\n    return provinceCode;\n  }\n\n  public void setProvinceCode(String provinceCode) {\n    this.provinceCode = provinceCode;\n  }\n\n  public String getCityCode() {\n    return cityCode;\n  }\n\n  public void setCityCode(String cityCode) {\n    this.cityCode = cityCode;\n  }\n\n  public List<Company> getCompanies() {\n    return companies;\n  }\n\n  public void setCompanies(List<Company> companies) {\n    this.companies = companies;\n  }\n\n  public List<Education> getEducations() {\n    return educations;\n  }\n\n  public void setEducations(List<Education> educations) {\n    this.educations = educations;\n  }\n\n  public Integer getFansNum() {\n    return fansNum;\n  }\n\n  public void setFansNum(Integer fansNum) {\n    this.fansNum = fansNum;\n  }\n\n  public Integer getFavNum() {\n    return favNum;\n  }\n\n  public void setFavNum(Integer favNum) {\n    this.favNum = favNum;\n  }\n\n  public String getHead() {\n    return head;\n  }\n\n  public void setHead(String head) {\n    this.head = head;\n  }\n\n  public String getHomeCountryCode() {\n    return homeCountryCode;\n  }\n\n  public void setHomeCountryCode(String homeCountryCode) {\n    this.homeCountryCode = homeCountryCode;\n  }\n\n  public String getHomeProvinceCode() {\n    return homeProvinceCode;\n  }\n\n  public void setHomeProvinceCode(String homeProvinceCode) {\n    this.homeProvinceCode = homeProvinceCode;\n  }\n\n  public String getHomeCityCode() {\n    return homeCityCode;\n  }\n\n  public void setHomeCityCode(String homeCityCode) {\n    this.homeCityCode = homeCityCode;\n  }\n\n  public String getHometownCode() {\n    return hometownCode;\n  }\n\n  public void setHometownCode(String hometownCode) {\n    this.hometownCode = hometownCode;\n  }\n\n  public String getHomepage() {\n    return homepage;\n  }\n\n  public void setHomepage(String homepage) {\n    this.homepage = homepage;\n  }\n\n  public Integer getIdolNum() {\n    return idolNum;\n  }\n\n  public void setIdolNum(Integer idolNum) {\n    this.idolNum = idolNum;\n  }\n\n  public String getIndustryCode() {\n    return industryCode;\n  }\n\n  public void setIndustryCode(String industryCode) {\n    this.industryCode = industryCode;\n  }\n\n  public String getIntroduction() {\n    return introduction;\n  }\n\n  public void setIntroduction(String introduction) {\n    this.introduction = introduction;\n  }\n\n  public Boolean getIsEnt() {\n    return isEnt;\n  }\n\n  public void setIsEnt(Boolean isEnt) {\n    this.isEnt = isEnt;\n  }\n\n  public Boolean getIsMyBlack() {\n    return isMyBlack;\n  }\n\n  public void setIsMyBlack(Boolean isMyBlack) {\n    this.isMyBlack = isMyBlack;\n  }\n\n  public Boolean getIsMyFans() {\n    return isMyFans;\n  }\n\n  public void setIsMyFans(Boolean isMyFans) {\n    this.isMyFans = isMyFans;\n  }\n\n  public Boolean getIsMyIdol() {\n    return isMyIdol;\n  }\n\n  public void setIsMyIdol(Boolean isMyIdol) {\n    this.isMyIdol = isMyIdol;\n  }\n\n  public Boolean getIsRealName() {\n    return isRealName;\n  }\n\n  public void setIsRealName(Boolean isRealName) {\n    this.isRealName = isRealName;\n  }\n\n  public Boolean getIsVip() {\n    return isVip;\n  }\n\n  public void setIsVip(Boolean isVip) {\n    this.isVip = isVip;\n  }\n\n  public String getLocation() {\n    return location;\n  }\n\n  public void setLocation(String location) {\n    this.location = location;\n  }\n\n  public Integer getMutualFansNum() {\n    return mutualFansNum;\n  }\n\n  public void setMutualFansNum(Integer mutualFansNum) {\n    this.mutualFansNum = mutualFansNum;\n  }\n\n  public SendPrivate getSendPrivate() {\n    return sendPrivate;\n  }\n\n  public void setSendPrivate(SendPrivate sendPrivate) {\n    this.sendPrivate = sendPrivate;\n  }\n\n  public Gender getGender() {\n    return gender;\n  }\n\n  public void setGender(Gender gender) {\n    this.gender = gender;\n  }\n\n  public List<Tag> getTags() {\n    return tags;\n  }\n\n  public void setTags(List<Tag> tags) {\n    this.tags = tags;\n  }\n\n  public List<TweetInfo> getTweets() {\n    return tweets;\n  }\n\n  public void setTweets(List<TweetInfo> tweets) {\n    this.tweets = tweets;\n  }\n\n  public Integer getTweetNum() {\n    return tweetNum;\n  }\n\n  public void setTweetNum(Integer tweetNum) {\n    this.tweetNum = tweetNum;\n  }\n\n  public String getVerifyInfo() {\n    return verifyInfo;\n  }\n\n  public void setVerifyInfo(String verifyInfo) {\n    this.verifyInfo = verifyInfo;\n  }\n\n  public Integer getExp() {\n    return exp;\n  }\n\n  public void setExp(Integer exp) {\n    this.exp = exp;\n  }\n\n  public Integer getLevel() {\n    return level;\n  }\n\n  public void setLevel(Integer level) {\n    this.level = level;\n  }\n\n  public String getSeqId() {\n    return seqId;\n  }\n\n  public void setSeqId(String seqId) {\n    this.seqId = seqId;\n  }\n\n  public static WeiboUser parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    WeiboUser obj = new WeiboUser(jsonObject);\n    obj.openId = Result.toString(jsonObject.get(\"openid\"));\n    obj.name = Result.toString(jsonObject.opt(\"name\"));\n    obj.nick = Result.toString(jsonObject.opt(\"nick\"));\n    obj.regTime = Result.parseTimeSeconds(jsonObject.opt(\"regtime\"));\n    Object year = jsonObject.get(\"birth_year\");\n    Object month = jsonObject.get(\"birth_month\");\n    Object day = jsonObject.get(\"birth_day\");\n    obj.birth =\n        Result.parseDate(year.toString() + month.toString() + day.toString(), \"yyyy-MM-dd\",\n            Locale.CHINA);\n    obj.countryCode = Result.toString(jsonObject.opt(\"country_code\"));\n    obj.provinceCode = Result.toString(jsonObject.opt(\"province_code\"));\n    obj.cityCode = Result.toString(jsonObject.opt(\"city_code\"));\n    JSONArray jsonArray = jsonObject.optJSONArray(\"comp\");\n    if (jsonArray != null) {\n      List<Company> companies = new ArrayList<Company>();\n      for (int i = 0; i < jsonArray.length(); i++) {\n        companies.add(Company.parse(jsonArray.optJSONObject(i)));\n      }\n      obj.companies = companies;\n    }\n    jsonArray = jsonObject.optJSONArray(\"edu\");\n    if (jsonArray != null) {\n      List<Education> educations = new ArrayList<Education>();\n      for (int i = 0; i < jsonArray.length(); i++) {\n        educations.add(Education.parse(jsonArray.optJSONObject(i)));\n      }\n      obj.educations = educations;\n    }\n    obj.fansNum = Result.parseInteger(jsonObject.opt(\"fansnum\"));\n    obj.favNum = Result.parseInteger(jsonObject.opt(\"favnum\"));\n    obj.head = Result.toString(jsonObject.opt(\"head\"));\n    obj.homeCountryCode = Result.toString(jsonObject.opt(\"homecountry_code\"));\n    obj.homeProvinceCode = Result.toString(jsonObject.opt(\"homeprovince_code\"));\n    obj.homeCityCode = Result.toString(jsonObject.opt(\"homecity_code\"));\n    obj.hometownCode = Result.toString(jsonObject.opt(\"hometown_code\"));\n    obj.homepage = Result.toString(jsonObject.opt(\"homepage\"));\n    obj.idolNum = Result.parseInteger(jsonObject.opt(\"idolnum\"));\n    obj.industryCode = Result.toString(jsonObject.opt(\"industry_code\"));\n    obj.introduction = Result.toString(jsonObject.opt(\"introduction\"));\n    obj.isEnt = Result.parseBoolean(jsonObject.opt(\"isent\"));\n    obj.isMyBlack = Result.parseBoolean(jsonObject.opt(\"ismyblack\"));\n    obj.isMyFans = Result.parseBoolean(jsonObject.opt(\"ismyfans\"));\n    obj.isMyIdol = Result.parseBoolean(jsonObject.opt(\"isMyIdol\"));\n    obj.isRealName = Result.parseBoolean(jsonObject.opt(\"isrealname\"));\n    obj.isVip = Result.parseBoolean(jsonObject.opt(\"isvip\"));\n    obj.location = Result.toString(jsonObject.opt(\"location\"));\n    obj.mutualFansNum = Result.parseInteger(jsonObject.opt(\"mutual_fans_num\"));\n    obj.sendPrivate = SendPrivate.parse(Result.parseInteger(jsonObject.opt(\"send_private_flag\")));\n    obj.gender = Gender.parse(Result.parseInteger(jsonObject.opt(\"sex\")));\n    jsonArray = jsonObject.optJSONArray(\"tag\");\n    if (jsonArray != null) {\n      List<Tag> tags = new ArrayList<Tag>();\n      for (int i = 0; i < jsonArray.length(); i++) {\n        tags.add(Tag.parse(jsonArray.optJSONObject(i)));\n      }\n      obj.tags = tags;\n    }\n    jsonArray = jsonObject.optJSONArray(\"tweetinfo\");\n    if (jsonArray == null) {\n      jsonArray = jsonObject.optJSONArray(\"tweet\");\n    }\n    if (jsonArray != null) {\n      List<TweetInfo> tweets = new ArrayList<TweetInfo>();\n      for (int i = 0; i < jsonArray.length(); i++) {\n        tweets.add(TweetInfo.parse(jsonArray.optJSONObject(i)));\n      }\n      obj.tweets = tweets;\n    }\n    obj.tweetNum = Result.parseInteger(jsonObject.opt(\"tweetnum\"));\n    obj.verifyInfo = Result.toString(jsonObject.opt(\"verifyinfo\"));\n    obj.exp = Result.parseInteger(jsonObject.opt(\"exp\"));\n    obj.level = Result.parseInteger(jsonObject.opt(\"level\"));\n    obj.seqId = Result.toString(jsonObject.opt(\"seqid\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/api/Contact.java",
    "content": "package com.belerweb.social.qq.mail.api;\n\nimport java.io.IOException;\nimport java.net.URLEncoder;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptContext;\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\n\nimport org.apache.commons.codec.DecoderException;\nimport org.apache.commons.codec.binary.Hex;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.RandomStringUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.config.RequestConfig;\nimport org.apache.http.client.entity.UrlEncodedFormEntity;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.message.BasicHeader;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.DataNode;\nimport org.jsoup.nodes.Document;\nimport org.jsoup.nodes.Element;\nimport org.jsoup.select.Elements;\n\nimport com.belerweb.social.captcha.api.Yundama;\nimport com.belerweb.social.captcha.bean.YundamaType;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.qq.mail.bean.Group;\nimport com.belerweb.social.qq.mail.bean.User;\nimport com.belerweb.social.qq.mail.bean.ValidationCode;\n\n/**\n * 获取QQ邮箱联系人\n */\npublic class Contact {\n  private static final Pattern PATTERN = Pattern.compile(\"sid=([0-9a-zA-Z-_]+)[&\\\"]\");\n\n  private HttpClient http;\n\n  private String email;\n  private String password;\n  private Yundama yundama;\n  private String sid;\n\n  /**\n   * 创建一个QQ邮箱联系人API\n   * \n   * @param email QQ邮箱地址\n   * @param password QQ邮箱密码\n   */\n  public Contact(String email, String password, Yundama yundama) {\n    this.email = email;\n    this.password = password;\n    this.yundama = yundama;\n    http = Http.newClient();\n  }\n\n  private List<Group> get(Group group) throws SocialException {\n    HttpGet request =\n        new HttpGet(\"http://mail.qq.com/cgi-bin/laddr_list?sid=\" + sid\n            + \"&operate=view&t=contact&view=\" + group.getType()\n            + (group.getId() != null ? (\"&groupid=\" + group.getId()) : \"&loc=frame_html,,,23\"));\n    request.addHeader(new BasicHeader(\"Referer\",\n        \"http://mail.qq.com/cgi-bin/laddr_list?operate=view&t=contact&view=normal&loc=frame_html,,,23&sid=\"\n            + sid));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    request\n        .addHeader(new BasicHeader(\"Referer\", \"http://mail.qq.com/cgi-bin/frame_html?sid=\" + sid));\n    try {\n      HttpResponse response = http.execute(request);\n      StatusLine statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      String result = IOUtils.toString(response.getEntity().getContent(), \"GB18030\");\n      request.releaseConnection();\n      List<Group> groups = null;\n      Document doc = Jsoup.parse(result);\n      Elements scripts = doc.select(\"script\");\n      for (Element el : scripts) {\n        for (DataNode node : el.dataNodes()) {\n          String script = node.getWholeData().trim();\n          if (script.startsWith(\"var goListData\")) {\n            ScriptEngine engine = new ScriptEngineManager().getEngineByName(\"javascript\");\n            List<User> users = new ArrayList<User>();\n            Bindings bindings = engine.createBindings();\n            bindings.put(\"users\", users);\n            script =\n                script\n                    + \"(function(){for(var i in goListData.oList){var o=goListData.oList[i];var u=new com.belerweb.social.qq.mail.bean.User();u.setId(o.sId);u.setType(o.sItemType);u.setLevel(o.sLevel);u.setName(o.oName.length?o.oName[0].sVal:'');u.setFamilyName((o.oFamilyName&&o.oFamilyName.length)?o.oFamilyName[0].sVal:'');u.setGivenName((o.oGivenName&&o.oGivenName.length)?o.oGivenName[0].sVal:'');u.setCustom((o.oCustom&&o.oCustom.length)?o.oCustom[0].sVal:'');u.setNickName((o.oQQNickName&&o.oQQNickName.length)?o.oQQNickName[0].sVal:'');u.setRelate((o.oRelate&&o.oRelate.length)?o.oRelate[0].sVal:'');u.setUrl((o.oUrl&&o.oUrl.length)?o.oUrl[0].sVal:'');u.setDate((o.oDate&&o.oDate.length)?o.oDate[0].sVal:'');u.setBirthday((o.oBirthday&&o.oBirthday.length)?o.oBirthday[0].sVal:'');u.setIm((o.oIM&&o.oIM.length)?o.oIM[0].sVal:'');u.setNote((o.oNote&&o.oNote.length)?o.oNote[0].sVal:'');var es=new java.util.ArrayList();for(var j in o.oEmail){var obj=o.oEmail[j];var e=new com.belerweb.social.qq.mail.bean.Email();e.setEmail(obj.sVal);e.setLabel(obj.sLabel?obj.sLabel:'');e.setType(obj.sType?obj.sType:'');es.add(e)}u.setEmails(es);var ts=new java.util.ArrayList();for(var j in o.oTel){var obj=o.oTel[j];var t=new com.belerweb.social.qq.mail.bean.Tel();t.setNum(obj.sVal);t.setLabel(obj.sLabel?obj.sLabel:'');t.setType(obj.sType?obj.sType:'');ts.add(t)}u.setTels(ts);if(o.oOrg&&o.oOrg.length){var org=new com.belerweb.social.qq.mail.bean.Org();org.setOrg1(o.oOrg[0].sOrg1);org.setOrg2(o.oOrg[0].sOrg2);org.setTitle(o.oOrg[0].sTitle);u.setOrg(org)}if(o.oAdr&&o.oAdr.length){var adr=new com.belerweb.social.qq.mail.bean.Address();adr.setLabel(o.oAdr[0].sLabel);adr.setType(o.oAdr[0].sType);adr.setCountry(o.oAdr[0].sCountry);adr.setProvince(o.oAdr[0].sProvince);adr.setCity(o.oAdr[0].sCity);adr.setStreet(o.oAdr[0].sStreet);adr.setPostcode(o.oAdr[0].sPostcode);u.setAddress(adr)}users.add(u)}})();\";\n            if (group.getId() == null) {\n              groups = new ArrayList<Group>();\n              bindings.put(\"groups\", groups);\n              script =\n                  script\n                      + \"(function(){for(var k in goGroupData){for(var i in goGroupData[k].oList){var obj=goGroupData[k].oList[i];var g=new com.belerweb.social.qq.mail.bean.Group();g.setId(obj.sId);g.setType(obj.sSubType);g.setName(obj.sName);groups.add(g);}}})();\";\n            }\n            engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);\n            engine.eval(script);\n            group.setUsers(users);\n          }\n        }\n      }\n      return groups;\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    } catch (ScriptException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 按分组获取所有QQ邮箱中的联系人，包括QQ联系人\n   */\n  public List<Group> get() throws SocialException {\n    List<Group> result = new ArrayList<Group>();\n    login();\n    Group group = new Group();\n    group.setType(\"normal\");\n    group.setName(\"全部\");\n    group.setOwner(email);\n    List<Group> groups = get(group);\n    result.add(group);\n    for (Group group2 : groups) {\n      get(group2);\n      group2.setOwner(email);\n      result.add(group2);\n    }\n    return result;\n  }\n\n  /**\n   * QQ邮箱登录，如果登录成功则返回sid，登录失败抛出异常\n   */\n  public String login() throws SocialException {\n    ValidationCode validationCode = check();\n    String vc = validationCode.getCode();\n    if (validationCode.need()) {\n      vc = yundama.decode(getValidationCode(), YundamaType.ALPHABETIC4).getResult();\n    }\n\n    try {\n      String code = DigestUtils.md5Hex(password).toUpperCase();\n      byte[] byte1 = Hex.decodeHex(code.toCharArray());\n      byte[] byte2 = Hex.decodeHex(validationCode.getUid().toCharArray());\n      byte[] bytes = new byte[byte1.length + byte2.length];\n      System.arraycopy(byte1, 0, bytes, 0, byte1.length);\n      System.arraycopy(byte2, 0, bytes, byte1.length, byte2.length);\n      code = DigestUtils.md5Hex(bytes).toUpperCase();\n      code = DigestUtils.md5Hex(code + vc.toUpperCase()).toUpperCase();\n\n      List<NameValuePair> params = new ArrayList<NameValuePair>();\n      params.add(new BasicNameValuePair(\"aid\", \"522005705\"));\n      params.add(new BasicNameValuePair(\"ptlang\", \"2052\"));\n      params.add(new BasicNameValuePair(\"js_type\", \"2\"));\n      params.add(new BasicNameValuePair(\"js_ver\", \"10009\"));\n      params.add(new BasicNameValuePair(\"fp\", \"loginerroralert\"));\n      params.add(new BasicNameValuePair(\"wording\", \"快速登录\"));\n      params.add(new BasicNameValuePair(\"mibao_css\", \"m_ptmail\"));\n      params.add(new BasicNameValuePair(\"u1\", URLEncoder.encode(\n          \"https://mail.qq.com/cgi-bin/login?vt=passport&vm=wpt&ft=ptlogin&lang=cn&ss=&validcnt=&clientaddr=\"\n              + email, \"UTF-8\")));\n      params.add(new BasicNameValuePair(\"css\",\n          \"https://mail.qq.com/zh_CN/htmledition/style/fast_login181b91.css\"));\n      params.add(new BasicNameValuePair(\"daid\", \"4\"));\n      params.add(new BasicNameValuePair(\"dummy\", \"\"));\n      params.add(new BasicNameValuePair(\"from_ui\", \"1\"));\n      params.add(new BasicNameValuePair(\"g\", \"1\"));\n      params.add(new BasicNameValuePair(\"h\", \"1\"));\n      params.add(new BasicNameValuePair(\"ptredirect\", \"1\"));\n      params.add(new BasicNameValuePair(\"t\", \"1\"));\n      params.add(new BasicNameValuePair(\"action\", \"4-20-\" + RandomStringUtils.randomNumeric(5)));\n      params.add(new BasicNameValuePair(\"p\", code));\n      params.add(new BasicNameValuePair(\"u\", email));\n      params.add(new BasicNameValuePair(\"u_domain\", email.substring(email.indexOf(\"@\"))));\n      params.add(new BasicNameValuePair(\"uin\", email.substring(0, email.indexOf(\"@\"))));\n      params.add(new BasicNameValuePair(\"verifycode\", vc));\n      HttpGet request =\n          new HttpGet(\"https://ssl.ptlogin2.qq.com/login?\" + StringUtils.join(params, \"&\"));\n      request\n          .addHeader(new BasicHeader(\"Referer\", \"https://mail.qq.com/cgi-bin/loginpage?lang=cn\"));\n      request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n      HttpResponse response = http.execute(request);\n      StatusLine statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      String result = IOUtils.toString(response.getEntity().getContent());\n      request.releaseConnection();\n      String[] str = result.substring(7, result.length() - 1).split(\",\");\n      if (!\"0\".equals(str[0].trim().substring(1, str[0].length() - 1))) {\n        throw new SocialException(str[4]);\n      }\n\n      request = new HttpGet(str[2].trim().substring(1, str[2].length() - 1));\n      response = http.execute(request);\n      statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      result = IOUtils.toString(response.getEntity().getContent(), \"GB18030\");\n      request.releaseConnection();\n      Matcher matcher = PATTERN.matcher(result);\n      if (matcher.find()) {\n        this.sid = matcher.group(1);\n      } else {\n        if (!result.contains(\"看不清\")) {\n          Document doc = Jsoup.parse(result);\n          Elements scripts = doc.select(\"script\");\n          boolean status = false;\n          for (Element el : scripts) {\n            for (DataNode node : el.dataNodes()) {\n              String script = node.getWholeData().trim();\n              if (script.startsWith(\"var urlHead\")) {\n                ScriptEngine engine = new ScriptEngineManager().getEngineByName(\"javascript\");\n                script = script.replace(\"window.location.replace(targetUrl);\", \"\");\n                try {\n                  engine.eval(script);\n                  String url = engine.get(\"targetUrl\").toString();\n                  request = new HttpGet(url);\n                  response = http.execute(request);\n                  result = IOUtils.toString(response.getEntity().getContent(), \"GB18030\");\n                  status = true;\n                } catch (Exception e) {\n                  e.printStackTrace();\n                }\n              }\n            }\n          }\n          if (!status) {\n            // throw new SocialException(\"没有获取到sid\");\n          }\n        }\n        vc = yundama.decode(getValidationCode2(), YundamaType.ALPHABETIC4).getResult();\n        params = new ArrayList<NameValuePair>();\n        params.add(new BasicNameValuePair(\"btlogin\", \"登录\"));\n        params.add(new BasicNameValuePair(\"delegate_url\", \"\"));\n        params.add(new BasicNameValuePair(\"sid\", \"\"));\n        params.add(new BasicNameValuePair(\"uin\", email.substring(0, email.indexOf(\"@\"))));\n        params.add(new BasicNameValuePair(\"verifycode\", vc));\n        params.add(new BasicNameValuePair(\"vt\", \"passport\"));\n        HttpPost post = new HttpPost(\"https://mail.qq.com/cgi-bin/login?sid=\");\n        post.setConfig(RequestConfig.custom().setRedirectsEnabled(false)\n            .setRelativeRedirectsAllowed(false).setCircularRedirectsAllowed(false).build());\n        post.setEntity(new UrlEncodedFormEntity(params));\n        response = http.execute(post);\n        try {\n          result = IOUtils.toString(response.getEntity().getContent(), \"GB18030\");\n          matcher = PATTERN.matcher(result);\n          if (matcher.find()) {\n            this.sid = matcher.group(1);\n          } else {\n            matcher = PATTERN.matcher(response.getFirstHeader(\"Location\").getValue());\n            if (matcher.find()) {\n              this.sid = matcher.group(1);\n            } else {\n              throw new SocialException(\"没有获取到sid\");\n            }\n          }\n        } catch (Exception e) {\n          throw new SocialException(\"没有获取到sid\");\n        }\n      }\n      return this.sid;\n    } catch (DecoderException e) {\n      throw new SocialException(e);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 检查QQ邮箱登录是否需要验证码\n   */\n  public ValidationCode check() throws SocialException {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"appid\", \"522005705\"));\n    params.add(new BasicNameValuePair(\"ptlang\", \"2052\"));\n    params.add(new BasicNameValuePair(\"js_type\", \"2\"));\n    params.add(new BasicNameValuePair(\"js_ver\", \"10009\"));\n    params.add(new BasicNameValuePair(\"r\", String.valueOf(Math.random())));\n    params.add(new BasicNameValuePair(\"uin\", email));\n    HttpGet request =\n        new HttpGet(\"https://ssl.ptlogin2.qq.com/check?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\", \"https://mail.qq.com/cgi-bin/loginpage?lang=cn\"));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    try {\n      HttpResponse response = http.execute(request);\n      StatusLine statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      String result = IOUtils.toString(response.getEntity().getContent());\n      request.releaseConnection();\n      return new ValidationCode(result);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 获取验证码\n   */\n  public byte[] getValidationCode() throws SocialException {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"aid\", \"522005705\"));\n    params.add(new BasicNameValuePair(\"r\", String.valueOf(Math.random())));\n    params.add(new BasicNameValuePair(\"uin\", email));\n    HttpGet request =\n        new HttpGet(\"https://ssl.captcha.qq.com/getimage?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\", \"https://mail.qq.com/cgi-bin/loginpage?lang=cn\"));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    try {\n      HttpResponse response = http.execute(request);\n      StatusLine statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      byte[] result = IOUtils.toByteArray(response.getEntity().getContent());\n      request.releaseConnection();\n      return result;\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 获取验证码2\n   */\n  public byte[] getValidationCode2() throws SocialException {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"aid\", \"23000101\"));\n    params.add(new BasicNameValuePair(\"f\", \"html\"));\n    params.add(new BasicNameValuePair(\"ck\", \"1\"));\n    params.add(new BasicNameValuePair(\"r\", String.valueOf(Math.random())));\n    HttpGet request =\n        new HttpGet(\"https://mail.qq.com/cgi-bin/getverifyimage?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\",\n        \"  https://mail.qq.com/cgi-bin/loginpage?errtype=3\"));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    try {\n      HttpResponse response = http.execute(request);\n      StatusLine statusLine = response.getStatusLine();\n      if (statusLine == null || statusLine.getStatusCode() != HttpStatus.SC_OK\n          || response.getEntity() == null) {\n        throw new SocialException(String.valueOf(statusLine));\n      }\n      byte[] result = IOUtils.toByteArray(response.getEntity().getContent());\n      request.releaseConnection();\n      return result;\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/Address.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\npublic class Address {\n\n  private String label;\n  private String type;\n  private String country;\n  private String province;\n  private String city;\n  private String street;\n  private String postcode;\n\n  public String getLabel() {\n    return label;\n  }\n\n  public void setLabel(String label) {\n    this.label = label;\n  }\n\n  public String getType() {\n    return type;\n  }\n\n  public void setType(String type) {\n    this.type = type;\n  }\n\n  public String getCountry() {\n    return country;\n  }\n\n  public void setCountry(String country) {\n    this.country = country;\n  }\n\n  public String getProvince() {\n    return province;\n  }\n\n  public void setProvince(String province) {\n    this.province = province;\n  }\n\n  public String getCity() {\n    return city;\n  }\n\n  public void setCity(String city) {\n    this.city = city;\n  }\n\n  public String getStreet() {\n    return street;\n  }\n\n  public void setStreet(String street) {\n    this.street = street;\n  }\n\n  public String getPostcode() {\n    return postcode;\n  }\n\n  public void setPostcode(String postcode) {\n    this.postcode = postcode;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/Email.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\npublic class Email {\n\n  private String label;\n  private String type;\n  private String email;\n\n  public String getLabel() {\n    return label;\n  }\n\n  public void setLabel(String label) {\n    this.label = label;\n  }\n\n  public String getType() {\n    return type;\n  }\n\n  public void setType(String type) {\n    this.type = type;\n  }\n\n  public String getEmail() {\n    return email;\n  }\n\n  public void setEmail(String email) {\n    this.email = email;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/Group.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\nimport java.util.List;\n\npublic class Group {\n\n  private String id;\n  private String type;\n  private String name;\n  private List<User> users;\n  private String owner;\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getType() {\n    return type;\n  }\n\n  public void setType(String type) {\n    this.type = type;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public List<User> getUsers() {\n    return users;\n  }\n\n  public void setUsers(List<User> users) {\n    this.users = users;\n  }\n\n  public String getOwner() {\n    return owner;\n  }\n\n  public void setOwner(String owner) {\n    this.owner = owner;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/Org.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\npublic class Org {\n\n  private String org1;\n  private String org2;\n  private String title;\n\n  public String getOrg1() {\n    return org1;\n  }\n\n  public void setOrg1(String org1) {\n    this.org1 = org1;\n  }\n\n  public String getOrg2() {\n    return org2;\n  }\n\n  public void setOrg2(String org2) {\n    this.org2 = org2;\n  }\n\n  public String getTitle() {\n    return title;\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/Tel.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\npublic class Tel {\n\n  private String label;\n  private String type;\n  private String num;\n\n  public String getLabel() {\n    return label;\n  }\n\n  public void setLabel(String label) {\n    this.label = label;\n  }\n\n  public String getType() {\n    return type;\n  }\n\n  public void setType(String type) {\n    this.type = type;\n  }\n\n  public String getNum() {\n    return num;\n  }\n\n  public void setNum(String num) {\n    this.num = num;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/User.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\nimport java.util.List;\n\npublic class User {\n\n  private String id;\n  private String level;\n  private String type;\n  private String name;\n  private String nickName;\n  private String familyName;\n  private String givenName;\n  private String relate;\n  private String url;\n  private String date;\n  private String birthday;\n  private String im;\n  private String custom;\n  private String note;\n  private Address address;\n  private Org org;\n  private List<Email> emails;\n  private List<Tel> tels;\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getLevel() {\n    return level;\n  }\n\n  public void setLevel(String level) {\n    this.level = level;\n  }\n\n  public String getType() {\n    return type;\n  }\n\n  public void setType(String type) {\n    this.type = type;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getNickName() {\n    return nickName;\n  }\n\n  public void setNickName(String nickName) {\n    this.nickName = nickName;\n  }\n\n  public String getFamilyName() {\n    return familyName;\n  }\n\n  public void setFamilyName(String familyName) {\n    this.familyName = familyName;\n  }\n\n  public String getGivenName() {\n    return givenName;\n  }\n\n  public void setGivenName(String givenName) {\n    this.givenName = givenName;\n  }\n\n  public String getRelate() {\n    return relate;\n  }\n\n  public void setRelate(String relate) {\n    this.relate = relate;\n  }\n\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  public String getDate() {\n    return date;\n  }\n\n  public void setDate(String date) {\n    this.date = date;\n  }\n\n  public String getBirthday() {\n    return birthday;\n  }\n\n  public void setBirthday(String birthday) {\n    this.birthday = birthday;\n  }\n\n  public String getIm() {\n    return im;\n  }\n\n  public void setIm(String im) {\n    this.im = im;\n  }\n\n  public String getCustom() {\n    return custom;\n  }\n\n  public void setCustom(String custom) {\n    this.custom = custom;\n  }\n\n  public String getNote() {\n    return note;\n  }\n\n  public void setNote(String note) {\n    this.note = note;\n  }\n\n  public Address getAddress() {\n    return address;\n  }\n\n  public void setAddress(Address address) {\n    this.address = address;\n  }\n\n  public Org getOrg() {\n    return org;\n  }\n\n  public void setOrg(Org org) {\n    this.org = org;\n  }\n\n  public List<Email> getEmails() {\n    return emails;\n  }\n\n  public void setEmails(List<Email> emails) {\n    this.emails = emails;\n  }\n\n  public List<Tel> getTels() {\n    return tels;\n  }\n\n  public void setTels(List<Tel> tels) {\n    this.tels = tels;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/mail/bean/ValidationCode.java",
    "content": "package com.belerweb.social.qq.mail.bean;\n\npublic class ValidationCode {\n\n  private String status;\n  private String code;\n  private String uid;\n\n  public ValidationCode() {}\n\n  public ValidationCode(String response) {\n    String[] str = response.substring(13, response.length() - 2).split(\",\");\n    status = str[0].trim().substring(1, str[0].length() - 1);\n    code = str[1].trim().substring(1, str[1].length() - 1);\n    uid = str[2].trim().substring(1, str[2].length() - 1).replaceAll(\"\\\\\\\\x\", \"\");\n  }\n\n  public String getStatus() {\n    return status;\n  }\n\n  public void setStatus(String status) {\n    this.status = status;\n  }\n\n  public String getCode() {\n    return code;\n  }\n\n  public void setCode(String code) {\n    this.code = code;\n  }\n\n  public String getUid() {\n    return uid;\n  }\n\n  public void setUid(String uid) {\n    this.uid = uid;\n  }\n\n  public boolean need() {\n    return !\"0\".equals(status);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/qzone/api/Visitor.java",
    "content": "package com.belerweb.social.qq.qzone.api;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.commons.codec.DecoderException;\nimport org.apache.commons.codec.binary.Hex;\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.Header;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.message.BasicHeader;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.json.JSONObject;\nimport org.jsoup.Jsoup;\nimport org.jsoup.nodes.Document;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.captcha.api.Yundama;\nimport com.belerweb.social.captcha.bean.YundamaType;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.http.HttpException;\nimport com.belerweb.social.qq.mail.bean.ValidationCode;\n\npublic class Visitor {\n\n  private static final Logger LOGGER = LoggerFactory.getLogger(Visitor.class);\n\n  private String qq;\n  private String password;\n  private Yundama yundama;\n  private HttpClient http;\n\n  private String loginUi;\n  private String loginSig;\n  private String skey;\n\n  private boolean session;\n\n  public Visitor(String qq, String password, Yundama yundama) {\n    this.qq = qq;\n    this.password = password;\n    this.yundama = yundama;\n    this.http = Http.newClient();\n  }\n\n  private void openLogin() throws ClientProtocolException, IOException, HttpException {\n    String uri = \"http://user.qzone.qq.com/\" + qq + \"/friendvisitor\";\n    HttpResponse response = http.execute(new HttpGet(uri));\n    if (!Http.isRequestSuccess(response)) {\n      throw new SocialException(\"Step 1, open login ui failed.\");\n    }\n\n    Document doc = Jsoup.parse(Http.responseToString(response));\n    loginUi = doc.select(\"#login_frame\").get(0).attr(\"src\");\n    HttpGet request = new HttpGet(loginUi);\n    request.addHeader(new BasicHeader(\"Referer\", uri));\n    response = http.execute(request);\n    if (!Http.isRequestSuccess(response)) {\n      throw new SocialException(\"Step 2, open login ui failed.\");\n    }\n\n    doc = Jsoup.parse(Http.responseToString(response));\n    Matcher matcher =\n        Pattern.compile(\"login_sig\\\\s*:\\\\s*[\\\"']([^\\\"']+)[\\\"']\").matcher(\n            doc.select(\"script\").get(0).html());\n    if (!matcher.find()) {\n      throw new SocialException(\"Step 3, login page isn't contain login_sig.\");\n    }\n    loginSig = matcher.group(1);\n  }\n\n  private ValidationCode loginCheck() throws ClientProtocolException, IOException, HttpException {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"regmaster\", \"\"));\n    params.add(new BasicNameValuePair(\"uin\", qq));\n    params.add(new BasicNameValuePair(\"appid\", \"549000912\"));\n    params.add(new BasicNameValuePair(\"js_ver\", \"10064\"));\n    params.add(new BasicNameValuePair(\"js_type\", \"1\"));\n    params.add(new BasicNameValuePair(\"login_sig\", loginSig));\n    params\n        .add(new BasicNameValuePair(\"u1\", \"http://qzs.qq.com/qzone/v5/loginsucc.html?para=reload\"));\n    params.add(new BasicNameValuePair(\"r\", String.valueOf(Math.random())));\n    HttpGet request =\n        new HttpGet(\"http://check.ptlogin2.qq.com/check?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\", loginUi));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    HttpResponse response = http.execute(request);\n    if (!Http.isRequestSuccess(response)) {\n      throw new SocialException(\"Step 4, check if validation code failed.\");\n    }\n    return new ValidationCode(Http.responseToString(response));\n  }\n\n  private byte[] getValidationCode() throws ClientProtocolException, IOException, HttpException {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"uin\", qq));\n    params.add(new BasicNameValuePair(\"aid\", \"549000912\"));\n    params.add(new BasicNameValuePair(String.valueOf(Math.random()), \"\"));\n    HttpGet request =\n        new HttpGet(\"http://captcha.qq.com/getimage?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\", loginUi));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    HttpResponse response = http.execute(request);\n    if (!Http.isRequestSuccess(response)) {\n      throw new SocialException(\"Step 5, get validation code image failed.\");\n    }\n    return IOUtils.toByteArray(response.getEntity().getContent());\n  }\n\n  private void login() throws ClientProtocolException, IOException, DecoderException, HttpException {\n    openLogin();\n    ValidationCode validationCode = loginCheck();\n    String code = validationCode.getCode();\n    if (validationCode.need()) {\n      LOGGER.debug(\"Qzone visitor need validation code.\");\n      code = yundama.decode(getValidationCode(), YundamaType.ALPHANUMERIC).getResult();\n      LOGGER.debug(\"Qzone visitor validation code is {}.\", code);\n    }\n\n    String p = DigestUtils.md5Hex(password).toUpperCase();\n    byte[] byte1 = Hex.decodeHex(p.toCharArray());\n    byte[] byte2 = Hex.decodeHex(validationCode.getUid().toCharArray());\n    byte[] bytes = new byte[byte1.length + byte2.length];\n    System.arraycopy(byte1, 0, bytes, 0, byte1.length);\n    System.arraycopy(byte2, 0, bytes, byte1.length, byte2.length);\n    p = DigestUtils.md5Hex(bytes).toUpperCase();\n    p = DigestUtils.md5Hex(p + code.toUpperCase()).toUpperCase();\n\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    params.add(new BasicNameValuePair(\"u\", qq));\n    params.add(new BasicNameValuePair(\"p\", p));\n    params.add(new BasicNameValuePair(\"verifycode\", code));\n    params.add(new BasicNameValuePair(\"aid\", \"549000912\"));\n    params.add(new BasicNameValuePair(\"u1\",\n        \"http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dreload\"));\n    params.add(new BasicNameValuePair(\"h\", \"1\"));\n    params.add(new BasicNameValuePair(\"ptredirect\", \"0\"));\n    params.add(new BasicNameValuePair(\"ptlang\", \"2052\"));\n    params.add(new BasicNameValuePair(\"from_ui\", \"1\"));\n    params.add(new BasicNameValuePair(\"dump\", \"\"));\n    params.add(new BasicNameValuePair(\"low_login_enable\", \"0\"));\n    params.add(new BasicNameValuePair(\"regmaster\", \"\"));\n    params.add(new BasicNameValuePair(\"fp\", \"loginerroralert\"));\n    params.add(new BasicNameValuePair(\"action\", \"2-22-1389885147889\"));\n    params.add(new BasicNameValuePair(\"mibao_css\", \"\"));\n    params.add(new BasicNameValuePair(\"t\", \"1\"));\n    params.add(new BasicNameValuePair(\"g\", \"1\"));\n    params.add(new BasicNameValuePair(\"js_ver\", \"10064\"));\n    params.add(new BasicNameValuePair(\"js_type\", \"1\"));\n    params.add(new BasicNameValuePair(\"login_sig\", loginSig));\n    params.add(new BasicNameValuePair(\"pt_rsa\", \"0\"));\n    HttpGet request = new HttpGet(\"http://ptlogin2.qq.com/login?\" + StringUtils.join(params, \"&\"));\n    request.addHeader(new BasicHeader(\"Referer\", loginUi));\n    request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n    HttpResponse response = http.execute(request);\n    if (!Http.isRequestSuccess(response)) {\n      throw new SocialException(\"Step 6, login failed.\");\n    }\n\n    LOGGER.debug(\"Qzone visitor login end.\");\n    String html = Http.responseToString(response);\n    if (!html.contains(\"登录成功\")) {\n      throw new SocialException(\"Step 7, login failed.\");\n    }\n\n    for (Header header : response.getHeaders(\"Set-Cookie\")) {\n      Matcher matcher = Pattern.compile(\"skey=([^;]+);\").matcher(header.getValue());\n      if (matcher.find()) {\n        skey = matcher.group(1);\n        session = true;\n        LOGGER.debug(\"Qzone visitor login success.\");\n      }\n    }\n    if (!session) {\n      throw new SocialException(\"Step 8, get skey failed.\");\n    }\n  }\n\n  public JSONObject getSimple() throws SocialException {\n    LOGGER.debug(\"Qzone visitor getSimple begin.\");\n    try {\n      if (!session) {\n        LOGGER.debug(\"Qzone visitor need login.\");\n        login();\n      } else {\n        LOGGER.debug(\"Qzone in session.\");\n      }\n      long gtk = 5381;\n      for (int i = 0, len = skey.length(); i < len; ++i) {\n        gtk += (gtk << 5) + skey.charAt(i);\n      }\n      gtk = gtk & 2147483647;\n\n      List<NameValuePair> params = new ArrayList<NameValuePair>();\n      params.add(new BasicNameValuePair(\"uin\", qq));\n      params.add(new BasicNameValuePair(\"mask\", \"2\"));\n      params.add(new BasicNameValuePair(\"clear\", \"1\"));\n      params.add(new BasicNameValuePair(\"mod\", \"8\"));\n      params.add(new BasicNameValuePair(\"fupdate\", \"1\"));\n      params.add(new BasicNameValuePair(\"random\", String.valueOf(Math.random())));\n      params.add(new BasicNameValuePair(\"g_tk\", String.valueOf(gtk)));\n      HttpGet request =\n          new HttpGet(\"http://g.qzone.qq.com/cgi-bin/friendshow/cgi_get_visitor_simple?\"\n              + StringUtils.join(params, \"&\"));\n      request.addHeader(new BasicHeader(\"Referer\",\n          \"http://ctc.qzs.qq.com/qzone/v8/pages/visitors/refuse.html\"));\n      request.addHeader(new BasicHeader(\"Accept-Language\", \"zh-cn,zh\"));\n      HttpResponse response = http.execute(request);\n      if (!Http.isRequestSuccess(response)) {\n        throw new SocialException(\"Step 9, get visitor simple failed.\");\n      }\n\n      LOGGER.debug(\"Qzone visitor getSimple end.\");\n      String html = Http.responseToString(response);\n      JSONObject json =\n          new JSONObject(html.substring(html.indexOf(\"{\"), html.lastIndexOf(\"}\") + 1));\n      if (json.getInt(\"code\") != 0) {\n        LOGGER.debug(\"Qzone visitor getSimple error: {}\", html);\n        session = false;\n        http = Http.newClient();\n        loginUi = null;\n        loginSig = null;\n        skey = null;\n        return getSimple();\n      }\n\n      LOGGER.debug(\"Qzone visitor getSimple success.\");\n      return json;\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    } catch (HttpException e) {\n      throw new SocialException(e);\n    } catch (DecoderException e) {\n      throw new SocialException(e);\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/t/api/OAuth2.java",
    "content": "package com.belerweb.social.qq.t.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\n\nimport com.belerweb.social.API;\n\npublic final class OAuth2 extends API {\n\n  OAuth2(QQT t) {\n    super(t);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link QQT} 从获取clientId，redirectUri，responseType为code ，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize() {\n    return authorize(t.getRedirectUri());\n  }\n\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link QQT} 从获取clientId，responseType为code ，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize(String redirectUri) {\n    return authorize(t.getClientId(), redirectUri, \"code\", null, null, null);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 文档地址：http://wiki.t.qq.com/使用authorization_code获取access_token\n   * \n   * @param clientId 必须，申请应用时分配的app_key\n   * @param redirectUri 必须，授权回调地址，必须和应用注册的地址一致(地址长度上限为256个字节)\n   * @param responseType 必须，授权类型，为code\n   * @param wap \n   *        主要用于指定手机授权页的版本，无此参数默认显示pc授权页面。wap=1时，跳转到wap1.0的授权页。wap=2时，跳转到wap2.0的授权页。不带本参数时，手机访问默认跳到wap2\n   *        .0的授权页\n   * @param state \n   *        用于保持请求和回调的状态，授权请求成功后原样带回给第三方。该参数用于防止csrf攻击（跨站请求伪造攻击），强烈建议第三方带上该参数。参数设置建议为简单随机数+session的方式\n   * @param forceLogin 针对pc授权页。forcelogin=true，强制弹出登录授权页面\n   *        forcelogin=false，用户已经登录并且已经授权第三方应用，则不再弹出授权页面 默认为forcelogin=true\n   */\n  public String authorize(String clientId, String redirectUri, String responseType, String wap,\n      String state, Boolean forceLogin) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    t.addParameter(params, \"client_id\", clientId);\n    t.addParameter(params, \"redirect_uri\", redirectUri);\n    t.addParameter(params, \"response_type\", responseType);\n    t.addNotNullParameter(params, \"wap\", wap);\n    t.addNotNullParameter(params, \"state\", state);\n    t.addTrueParameter(params, \"forcelogin\", forceLogin);\n    return \"https://open.t.qq.com/cgi-bin/oauth2/authorize?\" + StringUtils.join(params, \"&\");\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/qq/t/api/QQT.java",
    "content": "package com.belerweb.social.qq.t.api;\n\nimport com.belerweb.social.SDK;\n\npublic final class QQT extends SDK {\n\n  private String clientId;\n  private String clientSecret;\n  private String redirectUri;\n\n  public QQT(String clientId, String clientSecret) {\n    this.clientId = clientId;\n    this.clientSecret = clientSecret;\n  }\n\n  public QQT(String clientId, String clientSecret, String redirectUri) {\n    this(clientId, clientSecret);\n    this.redirectUri = redirectUri;\n  }\n\n  public String getClientId() {\n    return clientId;\n  }\n\n  public void setClientId(String clientId) {\n    this.clientId = clientId;\n  }\n\n  public String getClientSecret() {\n    return clientSecret;\n  }\n\n  public void setClientSecret(String clientSecret) {\n    this.clientSecret = clientSecret;\n  }\n\n  public String getRedirectUri() {\n    return redirectUri;\n  }\n\n  public void setRedirectUri(String redirectUri) {\n    this.redirectUri = redirectUri;\n  }\n\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/api/OAuth2.java",
    "content": "package com.belerweb.social.weibo.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.weibo.bean.AccessToken;\nimport com.belerweb.social.weibo.bean.Display;\nimport com.belerweb.social.weibo.bean.Scope;\nimport com.belerweb.social.weibo.bean.TokenInfo;\n\npublic final class OAuth2 extends API {\n\n  OAuth2(Weibo weibo) {\n    super(weibo);\n  }\n\n\n  /**\n   * OAuth2的authorize接口\n   * \n   * 从 {@link Weibo} 从获取clientId，redirectUri，scope 使用 {@link Scope#ALL}，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, Scope[], String, Display, Boolean, String)\n   */\n  public String authorize() {\n    return authorize(weibo.getRedirectUri());\n  }\n\n\n  /**\n   * OAuth2的authorize接口\n   * \n   * 从 {@link Weibo} 从获取clientId，scope 使用 {@link Scope#ALL}，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, Scope[], String, Display, Boolean, String)\n   * \n   * @param redirectUri 授权回调地址，站外应用需与设置的回调地址一致，站内应用需填写canvas page的地址。\n   */\n  public String authorize(String redirectUri) {\n    return authorize(weibo.getClientId(), redirectUri, new Scope[] {Scope.ALL}, null, null, null,\n        null);\n  }\n\n  /**\n   * OAuth2的authorize接口\n   * \n   * 文档地址：http://open.weibo.com/wiki/Oauth2/authorize\n   * \n   * @param clientId 申请应用时分配的AppKey。\n   * @param redirectUri 授权回调地址，站外应用需与设置的回调地址一致，站内应用需填写canvas page的地址。\n   * @param scope 申请scope权限所需参数，可一次申请多个scope权限，用逗号分隔。<a\n   *        href=\"http://open.weibo.com/wiki/Scope\">使用文档</a>\n   * @param state 用于保持请求和回调的状态，在回调时，会在Query\n   *        Parameter中回传该参数。开发者可以用这个参数验证请求有效性，也可以记录用户请求授权页前的位置。这个参数可用于防止跨站请求伪造（CSRF）攻击\n   * @param display 授权页面的终端类型，取值见下面的说明。\n   * @param forceLogin 是否强制用户重新登录，true：是，false：否。默认false。\n   * @param language 授权页语言，缺省为中文简体版，en为英文版。\n   */\n  public String authorize(String clientId, String redirectUri, Scope[] scope, String state,\n      Display display, Boolean forceLogin, String language) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addParameter(params, \"client_id\", clientId);\n    weibo.addParameter(params, \"redirect_uri\", redirectUri);\n    if (scope != null) {\n      weibo.addParameter(params, \"scope\", StringUtils.join(scope, \",\"));\n    }\n    weibo.addNotNullParameter(params, \"state\", state);\n    weibo.addNotNullParameter(params, \"display\", display);\n    weibo.addTrueParameter(params, \"forcelogin\", forceLogin);\n    weibo.addNotNullParameter(params, \"language\", language);\n\n    return \"https://api.weibo.com/oauth2/authorize?\" + StringUtils.join(params, \"&\");\n  }\n\n  /**\n   * 从 {@link Weibo} 从获取clientId，clientSecret，grantType，redirectUri 使用 authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String)\n   */\n  public Result<AccessToken> accessToken(String code) {\n    return accessToken(code, weibo.getRedirectUri());\n  }\n\n  /**\n   * 从 {@link Weibo} 从获取clientId，clientSecret，grantType 使用 authorization_code\n   * \n   * @see OAuth2#accessToken(String, String, String, String, String)\n   */\n  public Result<AccessToken> accessToken(String code, String redirectUri) {\n    return accessToken(weibo.getClientId(), weibo.getClientSecret(), \"authorization_code\", code,\n        redirectUri);\n  }\n\n  /**\n   * OAuth2的access_token接口\n   * \n   * 文档地址：http://open.weibo.com/wiki/OAuth2/access_token\n   * \n   * @param clientId 申请应用时分配的AppKey。\n   * @param clientSecret 申请应用时分配的AppSecret。\n   * @param grantType 请求的类型，填写authorization_code\n   * @param code grant_type为authorization_code时，调用authorize获得的code值。\n   * @param redirectUri grant_type为authorization_code时，回调地址，需需与注册应用里的回调地址一致。\n   */\n  public Result<AccessToken> accessToken(String clientId, String clientSecret, String grantType,\n      String code, String redirectUri) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addParameter(params, \"client_id\", clientId);\n    weibo.addParameter(params, \"client_secret\", clientSecret);\n    weibo.addParameter(params, \"grant_type\", grantType);\n    if (\"authorization_code\".equals(grantType)) {\n      weibo.addParameter(params, \"code\", code);\n      weibo.addParameter(params, \"redirect_uri\", redirectUri);\n    }\n    String result = weibo.post(\"https://api.weibo.com/oauth2/access_token\", params);\n    return Result.parse(result, AccessToken.class);\n  }\n\n  /**\n   * 查询用户access_token的授权相关信息，包括授权时间，过期时间和scope权限。\n   * \n   * 文档地址：http://open.weibo.com/wiki/Oauth2/get_token_info\n   * \n   * @param accessToken 用户授权时生成的access_token。\n   */\n  public Result<TokenInfo> getTokenInfo(String accessToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addParameter(params, \"access_token\", accessToken);\n    String result = weibo.post(\"https://api.weibo.com/oauth2/get_token_info\", params);\n    return Result.parse(result, TokenInfo.class);\n  }\n\n  /**\n   * 用于OAuth1.0 access token 更换至 OAuth2.0 access\n   * token，帮助开发者使用新版接口和OAuth2.0时平滑迁移用户。详细的OAuth1.0调用方式和SDK资源参见：<a\n   * href=\"http://open.weibo.com/wiki/Oauth\">http://open.weibo.com/wiki/Oauth</a>\n   * \n   * 文档地址：http://open.weibo.com/wiki/Oauth2/get_oauth2_token\n   * \n   * @param oauthConsumerKey 创建应用时生成的APP KEY。\n   * @param oauthToken oauth的token。\n   * @param oauthSignatureMethod 签名方法，建议使用“HMAC-SHA1”。\n   * @param oauthTimestamp 生成Base String时的时间戳。\n   * @param oauthNonce 单次值，一个随机字符串，防止重复攻击。该参数只支持ASCII码的字符串。\n   * @param oauthVersion OAuth协议版本。填写“1.0”。\n   * @param oauthSignature 签名值，是由根据上面的几个参数生成的 Base String经HMAC-SHA1算法计算得出。\n   */\n  public void getOAuth2Token(String oauthConsumerKey, String oauthToken,\n      String oauthSignatureMethod, Long oauthTimestamp, String oauthNonce, String oauthVersion,\n      String oauthSignature) {\n    throw new SocialException(\"方法还未实现...\");\n  }\n\n  /**\n   * 授权回收接口，帮助开发者主动取消用户的授权。\n   * \n   * 文档地址：http://open.weibo.com/wiki/Oauth2/revokeoauth2\n   * \n   * @param accessToken 用户授权应用的access_token\n   */\n  public Result<Boolean> revokeOAuth2(String accessToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addParameter(params, \"access_token\", accessToken);\n    String json = weibo.post(\"https://api.weibo.com/oauth2/revokeoauth2\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error == null) {\n      return new Result<Boolean>(Result.parseBoolean(jsonObject.get(\"result\")));\n    }\n\n    return new Result<Boolean>(error);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/api/User.java",
    "content": "package com.belerweb.social.weibo.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.weibo.bean.UserCounts;\n\n/**\n * 读取用户信息接口\n */\npublic final class User extends API {\n\n  protected User(Weibo weibo) {\n    super(weibo);\n  }\n\n  /**\n   * 获取用户信息/根据用户ID获取用户信息\n   * \n   * 访问级别：普通接口\n   * \n   * 频次限制：是\n   * \n   * 文档地址：https://api.weibo.com/2/users/show.json\n   * \n   * @param source 采用OAuth授权方式不需要此参数，其他授权方式为必填参数，数值为应用的AppKey。\n   * @param accessToken 采用OAuth授权方式为必填参数，其他授权方式不需要此参数，OAuth授权后获得。\n   * @param uid 需要查询的用户ID。\n   * @param screename 需要查询的用户昵称。\n   * \n   *        参数uid与screen_name二者必选其一，且只能选其一\n   */\n  public Result<com.belerweb.social.weibo.bean.User> show(String source, String accessToken,\n      String uid, String screenName) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addNotNullParameter(params, \"source\", source);\n    weibo.addNotNullParameter(params, \"access_token\", accessToken);\n    weibo.addNotNullParameter(params, \"uid\", uid);\n    weibo.addNotNullParameter(params, \"screen_name\", screenName);\n    String json = weibo.get(\"https://api.weibo.com/2/users/show.json\", params);\n    return Result.parse(json, com.belerweb.social.weibo.bean.User.class);\n  }\n\n  /**\n   * 通过个性域名获取用户信息/通过个性化域名获取用户资料以及用户最新的一条微博\n   * \n   * 访问级别：普通接口\n   * \n   * 频次限制：是\n   * \n   * 文档地址：https://api.weibo.com/2/users/domain_show.json\n   * \n   * @param source 采用OAuth授权方式不需要此参数，其他授权方式为必填参数，数值为应用的AppKey。\n   * @param accessToken 采用OAuth授权方式为必填参数，其他授权方式不需要此参数，OAuth授权后获得。\n   * @param domain 需要查询的个性化域名。\n   */\n  public Result<com.belerweb.social.weibo.bean.User> domainShow(String source, String accessToken,\n      String domain) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addNotNullParameter(params, \"source\", source);\n    weibo.addNotNullParameter(params, \"access_token\", accessToken);\n    weibo.addParameter(params, \"domain\", domain);\n    String json = weibo.get(\"https://api.weibo.com/2/users/domain_show.json\", params);\n    return Result.parse(json, com.belerweb.social.weibo.bean.User.class);\n  }\n\n  /**\n   * 批量获取用户的粉丝数、关注数、微博数\n   * \n   * 访问级别：普通接口\n   * \n   * 频次限制：是\n   * \n   * 文档地址：https://api.weibo.com/2/users/counts.json\n   * \n   * @param source 采用OAuth授权方式不需要此参数，其他授权方式为必填参数，数值为应用的AppKey。\n   * @param accessToken 采用OAuth授权方式为必填参数，其他授权方式不需要此参数，OAuth授权后获得。\n   * @param uids 需要获取数据的用户UID，最多不超过100个。\n   */\n  public Result<UserCounts> counts(String source, String accessToken, List<String> uids) {\n    if (uids == null || uids.size() > 100) {\n      throw new SocialException(\"需要获取数据的用户UID，必须且最多不超过100个\");\n    }\n\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weibo.addNotNullParameter(params, \"source\", source);\n    weibo.addNotNullParameter(params, \"access_token\", accessToken);\n    weibo.addParameter(params, \"uids\", StringUtils.join(uids, \",\"));\n    String result = weibo.get(\"https://api.weibo.com/2/users/counts.json\", params);\n    return Result.parse(result, UserCounts.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/api/Weibo.java",
    "content": "package com.belerweb.social.weibo.api;\n\nimport com.belerweb.social.SDK;\n\npublic final class Weibo extends SDK {\n\n  private String clientId;\n  private String clientSecret;\n  private String redirectUri;\n\n  private OAuth2 oAuth2;\n  private User user;\n\n  public Weibo(String clientId, String clientSecret) {\n    this.clientId = clientId;\n    this.clientSecret = clientSecret;\n  }\n\n\n  public Weibo(String clientId, String clientSecret, String redirectUri) {\n    this(clientId, clientSecret);\n    this.redirectUri = redirectUri;\n  }\n\n\n  public OAuth2 getOAuth2() {\n    if (oAuth2 == null) {\n      oAuth2 = new OAuth2(this);\n    }\n\n    return oAuth2;\n  }\n\n  public User getUser() {\n    if (user == null) {\n      user = new User(this);\n    }\n\n    return user;\n  }\n\n  public String getClientId() {\n    return clientId;\n  }\n\n  public void setClientId(String clientId) {\n    this.clientId = clientId;\n  }\n\n  public String getClientSecret() {\n    return clientSecret;\n  }\n\n  public void setClientSecret(String clientSecret) {\n    this.clientSecret = clientSecret;\n  }\n\n  public String getRedirectUri() {\n    return redirectUri;\n  }\n\n  public void setRedirectUri(String redirectUri) {\n    this.redirectUri = redirectUri;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/AccessToken.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class AccessToken extends JsonBean {\n\n  public AccessToken() {}\n\n  private AccessToken(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String token;// 用于调用access_token，接口获取授权后的access token。\n  private Long expiresIn;// access_token的生命周期，单位是秒数。\n  private Long remindIn;// access_token的生命周期（该参数即将废弃，开发者请使用expires_in）。\n  private String uid;// 当前授权用户的UID。\n\n  /**\n   * 用于调用access_token，接口获取授权后的access token。\n   */\n  public String getToken() {\n    return token;\n  }\n\n  public void setToken(String token) {\n    this.token = token;\n  }\n\n  /**\n   * access_token的生命周期，单位是秒数。\n   */\n  public Long getExpiresIn() {\n    return expiresIn;\n  }\n\n  public void setExpiresIn(Long expiresIn) {\n    this.expiresIn = expiresIn;\n  }\n\n  /**\n   * access_token的生命周期（该参数即将废弃，开发者请使用expires_in）。\n   */\n  public Long getRemindIn() {\n    return remindIn;\n  }\n\n  public void setRemindIn(Long remindIn) {\n    this.remindIn = remindIn;\n  }\n\n  /**\n   * 当前授权用户的UID。\n   */\n  public String getUid() {\n    return uid;\n  }\n\n  public void setUid(String uid) {\n    this.uid = uid;\n  }\n\n  public static AccessToken parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    AccessToken obj = new AccessToken(jsonObject);\n    obj.token = jsonObject.getString(\"access_token\");\n    obj.expiresIn = Result.parseLong(jsonObject.opt(\"expires_in\"));\n    obj.remindIn = Result.parseLong(jsonObject.opt(\"remind_in\"));\n    obj.uid = Result.toString(jsonObject.get(\"uid\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Comment.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport java.util.Date;\nimport java.util.Locale;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 评论\n * \n * 文档地址：http://open.weibo.com/wiki/常见返回对象数据结构#.E8.AF.84.E8.AE.BA.EF.BC.88comment.EF.BC.89\n */\npublic class Comment extends JsonBean {\n\n  public Comment() {}\n\n  private Comment(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 评论的ID\n  private String mid;// 评论的MID\n  private String idstr;// 字符串型的评论ID\n  private Date createdAt;// 评论创建时间\n  private String text;// 评论的内容\n  private String source;// 评论的来源\n  private User user;// 评论作者的用户信息字段\n  private Status status;// 评论的微博信息字段\n  private Comment replyComment;// 评论来源评论，当本评论属于对另一评论的回复时返回此字段\n\n  /**\n   * 评论的ID\n   */\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  /**\n   * 评论的MID\n   */\n  public String getMid() {\n    return mid;\n  }\n\n  public void setMid(String mid) {\n    this.mid = mid;\n  }\n\n  /**\n   * 字符串型的评论ID\n   */\n  public String getIdstr() {\n    return idstr;\n  }\n\n  public void setIdstr(String idstr) {\n    this.idstr = idstr;\n  }\n\n  /**\n   * 评论创建时间\n   */\n  public Date getCreatedAt() {\n    return createdAt;\n  }\n\n  public void setCreatedAt(Date createdAt) {\n    this.createdAt = createdAt;\n  }\n\n  /**\n   * 评论的内容\n   */\n  public String getText() {\n    return text;\n  }\n\n  public void setText(String text) {\n    this.text = text;\n  }\n\n  /**\n   * 评论的来源\n   */\n  public String getSource() {\n    return source;\n  }\n\n  public void setSource(String source) {\n    this.source = source;\n  }\n\n  /**\n   * 评论作者的用户信息字段\n   */\n  public User getUser() {\n    return user;\n  }\n\n  public void setUser(User user) {\n    this.user = user;\n  }\n\n  /**\n   * 评论的微博信息字段\n   */\n  public Status getStatus() {\n    return status;\n  }\n\n  public void setStatus(Status status) {\n    this.status = status;\n  }\n\n  /**\n   * 评论来源评论，当本评论属于对另一评论的回复时返回此字段\n   */\n  public Comment getReplyComment() {\n    return replyComment;\n  }\n\n  public void setReplyComment(Comment replyComment) {\n    this.replyComment = replyComment;\n  }\n\n  public static Comment parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Comment obj = new Comment(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.mid = Result.toString(jsonObject.opt(\"mid\"));\n    obj.idstr = Result.toString(jsonObject.opt(\"idstr\"));\n    obj.createdAt =\n        Result\n            .parseDate(jsonObject.opt(\"created_at\"), \"EEE MMM dd HH:mm:ss Z yyyy\", Locale.ENGLISH);\n\n    obj.text = Result.toString(jsonObject.get(\"text\"));\n    obj.source = Result.toString(jsonObject.opt(\"source\"));\n\n    obj.user = User.parse(jsonObject.optJSONObject(\"user\"));\n    obj.status = Status.parse(jsonObject);\n    obj.replyComment = Comment.parse(jsonObject.optJSONObject(\"reply_comment\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Display.java",
    "content": "package com.belerweb.social.weibo.bean;\n\n/**\n * 授权页面的终端类型\n */\npublic enum Display {\n\n  /**\n   * 默认的授权页面，适用于web浏览器。\n   */\n  DEFAULT(\"default\"),\n\n  /**\n   * 移动终端的授权页面，适用于支持html5的手机。注：使用此版授权页请用 https://open.weibo.cn/oauth2/authorize 授权接口\n   */\n  MOBILE(\"mobile\"),\n\n  /**\n   * wap版授权页面，适用于非智能手机。\n   */\n  WAP(\"wap\"),\n\n  /**\n   * 客户端版本授权页面，适用于PC桌面应用。\n   */\n  CLIENT(\"client\"),\n\n  /**\n   * 默认的站内应用授权页，授权后不返回access_token，只刷新站内应用父框架。\n   */\n  APPONWEIBO(\"apponweibo\");\n\n  private String display;\n\n  private Display(String display) {\n    this.display = display;\n  }\n\n  public String value() {\n    return display;\n  }\n\n  @Override\n  public String toString() {\n    return display;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Geo.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 地理信息\n * \n * 文档地址：http://open.weibo.com/wiki/常见返回对象数据结构#.E5.9C.B0.E7.90.86.E4.BF.A1.E6.81.AF.EF.BC.88geo.EF.BC\n * .89\n */\npublic class Geo extends JsonBean {\n\n  public Geo() {}\n\n  private Geo(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Double longitude;// 经度坐标\n  private Double latitude;// 维度坐标\n  private String city;// 所在城市的城市代码\n  private String province;// 所在省份的省份代码\n  private String cityName;// 所在城市的城市名称\n  private String provinceName;// 所在省份的省份名称\n  private String address;// 所在的实际地址，可以为空\n  private String pinyin;// 地址的汉语拼音，不是所有情况都会返回该字段\n  private String more;// 更多信息，不是所有情况都会返回该字段\n\n  /**\n   * 经度坐标\n   */\n  public Double getLongitude() {\n    return longitude;\n  }\n\n  public void setLongitude(Double longitude) {\n    this.longitude = longitude;\n  }\n\n  /**\n   * 维度坐标\n   */\n  public Double getLatitude() {\n    return latitude;\n  }\n\n  public void setLatitude(Double latitude) {\n    this.latitude = latitude;\n  }\n\n  /**\n   * 所在城市的城市代码\n   */\n  public String getCity() {\n    return city;\n  }\n\n  public void setCity(String city) {\n    this.city = city;\n  }\n\n  /**\n   * 所在省份的省份代码\n   */\n  public String getProvince() {\n    return province;\n  }\n\n  public void setProvince(String province) {\n    this.province = province;\n  }\n\n  /**\n   * 所在城市的城市名称\n   */\n  public String getCityName() {\n    return cityName;\n  }\n\n  public void setCityName(String cityName) {\n    this.cityName = cityName;\n  }\n\n  /**\n   * 所在省份的省份名称\n   */\n  public String getProvinceName() {\n    return provinceName;\n  }\n\n  public void setProvinceName(String provinceName) {\n    this.provinceName = provinceName;\n  }\n\n  /**\n   * 所在的实际地址，可以为空\n   */\n  public String getAddress() {\n    return address;\n  }\n\n  public void setAddress(String address) {\n    this.address = address;\n  }\n\n  /**\n   * 地址的汉语拼音，不是所有情况都会返回该字段\n   */\n  public String getPinyin() {\n    return pinyin;\n  }\n\n  public void setPinyin(String pinyin) {\n    this.pinyin = pinyin;\n  }\n\n  /**\n   * 更多信息，不是所有情况都会返回该字段\n   */\n  public String getMore() {\n    return more;\n  }\n\n  public void setMore(String more) {\n    this.more = more;\n  }\n\n  public static Geo parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Geo obj = new Geo(jsonObject);\n    obj.longitude = Result.parseDouble(jsonObject.get(\"longitude\"));\n    obj.latitude = Result.parseDouble(jsonObject.get(\"latitude\"));\n    obj.city = Result.toString(jsonObject.opt(\"city\"));\n    obj.province = Result.toString(jsonObject.opt(\"province\"));\n    obj.cityName = Result.toString(jsonObject.opt(\"city_name\"));\n    obj.provinceName = Result.toString(jsonObject.opt(\"province_name\"));\n    obj.address = Result.toString(jsonObject.opt(\"address\"));\n    obj.pinyin = Result.toString(jsonObject.opt(\"pinyin\"));\n    obj.more = Result.toString(jsonObject.opt(\"more\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Privacy.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 隐私设置\n */\npublic class Privacy extends JsonBean {\n\n  public Privacy() {}\n\n  private Privacy(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Integer comment;// 是否可以评论我的微博，0：所有人、1：关注的人、2：可信用户\n  private Integer geo;// 是否开启地理信息，0：不开启、1：开启\n  private Integer message;// 是否可以给我发私信，0：所有人、1：我关注的人、2：可信用户\n  private Integer realname;// 是否可以通过真名搜索到我，0：不可以、1：可以\n  private Integer badge;// 勋章是否可见，0：不可见、1：可见\n  private Integer mobile;// 是否可以通过手机号码搜索到我，0：不可以、1：可以\n  private Integer webim;// 是否开启webim， 0：不开启、1：开启\n\n  /**\n   * 是否可以评论我的微博，0：所有人、1：关注的人、2：可信用户\n   */\n  public Integer getComment() {\n    return comment;\n  }\n\n  public void setComment(Integer comment) {\n    this.comment = comment;\n  }\n\n  /**\n   * 是否开启地理信息，0：不开启、1：开启\n   */\n  public Integer getGeo() {\n    return geo;\n  }\n\n  public void setGeo(Integer geo) {\n    this.geo = geo;\n  }\n\n  /**\n   * 是否可以给我发私信，0：所有人、1：我关注的人、2：可信用户\n   */\n  public Integer getMessage() {\n    return message;\n  }\n\n  public void setMessage(Integer message) {\n    this.message = message;\n  }\n\n  /**\n   * 是否可以通过真名搜索到我，0：不可以、1：可以\n   */\n  public Integer getRealname() {\n    return realname;\n  }\n\n  public void setRealname(Integer realname) {\n    this.realname = realname;\n  }\n\n  /**\n   * 勋章是否可见，0：不可见、1：可见\n   */\n  public Integer getBadge() {\n    return badge;\n  }\n\n  public void setBadge(Integer badge) {\n    this.badge = badge;\n  }\n\n  /**\n   * 是否可以通过手机号码搜索到我，0：不可以、1：可以\n   */\n  public Integer getMobile() {\n    return mobile;\n  }\n\n  public void setMobile(Integer mobile) {\n    this.mobile = mobile;\n  }\n\n  /**\n   * 是否开启webim， 0：不开启、1：开启\n   */\n  public Integer getWebim() {\n    return webim;\n  }\n\n  public void setWebim(Integer webim) {\n    this.webim = webim;\n  }\n\n  public static Privacy parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Privacy obj = new Privacy(jsonObject);\n    obj.comment = Result.parseInteger(jsonObject.opt(\"comment\"));\n    obj.geo = Result.parseInteger(jsonObject.opt(\"geo\"));\n    obj.message = Result.parseInteger(jsonObject.opt(\"message\"));\n    obj.realname = Result.parseInteger(jsonObject.opt(\"realname\"));\n    obj.badge = Result.parseInteger(jsonObject.opt(\"badge\"));\n    obj.mobile = Result.parseInteger(jsonObject.opt(\"mobile\"));\n    obj.webim = Result.parseInteger(jsonObject.opt(\"webim\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Remind.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 消息未读数\n */\npublic class Remind extends JsonBean {\n\n  public Remind() {}\n\n  private Remind(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Integer status;// 新微博未读数\n  private Integer follower;// 新粉丝数\n  private Integer cmt;// 新评论数\n  private Integer dm;// 新私信数\n  private Integer mentionStatus;// 新提及我的微博数\n  private Integer mentionCmt;// 新提及我的评论数\n  private Integer group;// 微群消息未读数\n  private Integer privateGroup;// 私有微群消息未读数\n  private Integer notice;// 新通知未读数\n  private Integer invite;// 新邀请未读数\n  private Integer badge;// 新勋章数\n  private Integer photo;// 相册消息未读数\n  private Integer msgbox;// 消息未读数\n\n  /**\n   * 新微博未读数\n   */\n  public Integer getStatus() {\n    return status;\n  }\n\n  public void setStatus(Integer status) {\n    this.status = status;\n  }\n\n  /**\n   * 新粉丝数\n   */\n  public Integer getFollower() {\n    return follower;\n  }\n\n  public void setFollower(Integer follower) {\n    this.follower = follower;\n  }\n\n  /**\n   * 新评论数\n   */\n  public Integer getCmt() {\n    return cmt;\n  }\n\n  public void setCmt(Integer cmt) {\n    this.cmt = cmt;\n  }\n\n  /**\n   * 新私信数\n   */\n  public Integer getDm() {\n    return dm;\n  }\n\n  public void setDm(Integer dm) {\n    this.dm = dm;\n  }\n\n  /**\n   * 新提及我的微博数\n   */\n  public Integer getMentionStatus() {\n    return mentionStatus;\n  }\n\n  public void setMentionStatus(Integer mentionStatus) {\n    this.mentionStatus = mentionStatus;\n  }\n\n  /**\n   * 新提及我的评论数\n   */\n  public Integer getMentionCmt() {\n    return mentionCmt;\n  }\n\n  public void setMentionCmt(Integer mentionCmt) {\n    this.mentionCmt = mentionCmt;\n  }\n\n  /**\n   * 微群消息未读数\n   */\n  public Integer getGroup() {\n    return group;\n  }\n\n  public void setGroup(Integer group) {\n    this.group = group;\n  }\n\n  /**\n   * 私有微群消息未读数\n   */\n  public Integer getPrivateGroup() {\n    return privateGroup;\n  }\n\n  public void setPrivateGroup(Integer privateGroup) {\n    this.privateGroup = privateGroup;\n  }\n\n  /**\n   * 新通知未读数\n   */\n  public Integer getNotice() {\n    return notice;\n  }\n\n  public void setNotice(Integer notice) {\n    this.notice = notice;\n  }\n\n  /**\n   * 新邀请未读数\n   */\n  public Integer getInvite() {\n    return invite;\n  }\n\n  public void setInvite(Integer invite) {\n    this.invite = invite;\n  }\n\n  /**\n   * 新勋章数\n   */\n  public Integer getBadge() {\n    return badge;\n  }\n\n  public void setBadge(Integer badge) {\n    this.badge = badge;\n  }\n\n  /**\n   * 相册消息未读数\n   */\n  public Integer getPhoto() {\n    return photo;\n  }\n\n  public void setPhoto(Integer photo) {\n    this.photo = photo;\n  }\n\n  /**\n   * 消息未读数\n   */\n  public Integer getMsgbox() {\n    return msgbox;\n  }\n\n  public void setMsgbox(Integer msgbox) {\n    this.msgbox = msgbox;\n  }\n\n  public static Remind parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Remind obj = new Remind(jsonObject);\n    obj.status = Result.parseInteger(jsonObject.opt(\"status\"));\n    obj.follower = Result.parseInteger(jsonObject.opt(\"follower\"));\n    obj.cmt = Result.parseInteger(jsonObject.opt(\"cmt\"));\n    obj.dm = Result.parseInteger(jsonObject.opt(\"dm\"));\n    obj.mentionStatus = Result.parseInteger(jsonObject.opt(\"mention_status\"));\n    obj.mentionCmt = Result.parseInteger(jsonObject.opt(\"mention_cmt\"));\n    obj.group = Result.parseInteger(jsonObject.opt(\"group\"));\n    obj.privateGroup = Result.parseInteger(jsonObject.opt(\"private_group\"));\n    obj.notice = Result.parseInteger(jsonObject.opt(\"notice\"));\n    obj.invite = Result.parseInteger(jsonObject.opt(\"invite\"));\n    obj.badge = Result.parseInteger(jsonObject.opt(\"badge\"));\n    obj.photo = Result.parseInteger(jsonObject.opt(\"photo\"));\n    obj.msgbox = Result.parseInteger(jsonObject.opt(\"msgbox\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Scope.java",
    "content": "package com.belerweb.social.weibo.bean;\n\n/**\n * scope是OAuth2.0授权机制中authorize接口的一个参数\n * \n * 通过scope，平台将开放更多的微博核心功能给开发者，同时也加强用户隐私保护，提升了用户体验，用户在新OAuth2.0授权页中有权利选择赋予应用的功能。\n */\npublic enum Scope {\n\n  /**\n   * 请求下列所有scope权限\n   */\n  ALL(\"all\"),\n\n  /**\n   * 用户的联系邮箱\n   */\n  EMAIL(\"email\"),\n\n  /**\n   * 私信发送接口\n   */\n  DIRECT_MESSAGES_WRITE(\"direct_messages_write\"),\n\n  /**\n   * 私信读取接口\n   */\n  DIRECT_MESSAGES_READ(\"direct_messages_read\"),\n\n  /**\n   * 邀请发送接口\n   */\n  INVITATION_WRITE(\"invitation_write\"),\n\n  /**\n   * 好友分组读取接口组\n   */\n  FRIENDSHIPS_GROUPS_READ(\"friendships_groups_read\"),\n\n  /**\n   * 好友分组写入接口组\n   */\n  FRIENDSHIPS_GROUPS_WRITE(\"friendships_groups_write\"),\n\n  /**\n   * 定向微博读取接口组\n   */\n  STATUSES_TO_ME_READ(\"statuses_to_me_read\"),\n\n  /**\n   * 关注应用官方微博，该参数不对应具体接口，只需在应用控制台填写官方帐号即可（默认值是应用开发者帐号）\n   */\n  FOLLOW_APP_OFFICIAL_MICROBLOG(\"follow_app_official_microblog\");\n\n  private String scope;\n\n  private Scope(String scope) {\n    this.scope = scope;\n  }\n\n  public String value() {\n    return scope;\n  }\n\n  @Override\n  public String toString() {\n    return scope;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Status.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 微博\n * \n * 文档地址：http://open.weibo.com/wiki/常见返回对象数据结构#.E5.BE.AE.E5.8D.9A.EF.BC.88status.EF.BC.89\n */\npublic class Status extends JsonBean {\n\n  public Status() {}\n\n  private Status(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 微博ID\n  private String mid;// 微博MID\n  private String idstr;// 字符串型的微博ID\n  private Date createdAt;// 微博创建时间\n  private String text;// 微博信息内容\n  private String source;// 微博来源\n  private Boolean favorited;// 是否已收藏\n  private Boolean truncated;// 是否被截断\n  private String inReplyToStatusId;// （暂未支持）回复ID\n  private String inReplyToUserId;// 暂未支持）回复人UID\n  private String inReplyToScreenName;// 暂未支持）回复人昵称\n  private String thumbnailPic;// 缩略图片地址，没有时不返回此字段\n  private String bmiddlePic;// 中等尺寸图片地址，没有时不返回此字段\n  private String originalPic;// 原始图片地址，没有时不返回此字段\n  private Geo geo;// 地理信息字段\n  private User user;// 微博作者的用户信息字段\n  private Status retweetedStatus;// 被转发的原微博信息字段，当该微博为转发微博时返回\n  private Integer repostsCount;// 转发数\n  private Integer commentsCount;// 评论数\n  private Integer attitudesCount;// 表态数\n  private Integer mlevel;// 暂未支持\n  private Visible visible;// 微博的可见性及指定可见分组信息\n  private List<String> picUrls;// 微博配图地址。多图时返回多图链接。无配图返回“[]”\n  private List<String> ad;// 微博流内的推广微博ID\n\n  /**\n   * 微博ID\n   */\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  /**\n   * 微博MID\n   */\n  public String getMid() {\n    return mid;\n  }\n\n  public void setMid(String mid) {\n    this.mid = mid;\n  }\n\n  /**\n   * 字符串型的微博ID\n   */\n  public String getIdstr() {\n    return idstr;\n  }\n\n  public void setIdstr(String idstr) {\n    this.idstr = idstr;\n  }\n\n  /**\n   * 微博创建时间\n   */\n  public Date getCreatedAt() {\n    return createdAt;\n  }\n\n  public void setCreatedAt(Date createdAt) {\n    this.createdAt = createdAt;\n  }\n\n  /**\n   * 微博信息内容\n   */\n  public String getText() {\n    return text;\n  }\n\n  public void setText(String text) {\n    this.text = text;\n  }\n\n  /**\n   * 微博来源\n   */\n  public String getSource() {\n    return source;\n  }\n\n  public void setSource(String source) {\n    this.source = source;\n  }\n\n  /**\n   * 是否已收藏\n   */\n  public Boolean getFavorited() {\n    return favorited;\n  }\n\n  public void setFavorited(Boolean favorited) {\n    this.favorited = favorited;\n  }\n\n  /**\n   * 是否被截断\n   */\n  public Boolean getTruncated() {\n    return truncated;\n  }\n\n  public void setTruncated(Boolean truncated) {\n    this.truncated = truncated;\n  }\n\n  /**\n   * （暂未支持）回复ID\n   */\n  public String getInReplyToStatusId() {\n    return inReplyToStatusId;\n  }\n\n  public void setInReplyToStatusId(String inReplyToStatusId) {\n    this.inReplyToStatusId = inReplyToStatusId;\n  }\n\n  /**\n   * （暂未支持）回复人UID\n   */\n  public String getInReplyToUserId() {\n    return inReplyToUserId;\n  }\n\n  public void setInReplyToUserId(String inReplyToUserId) {\n    this.inReplyToUserId = inReplyToUserId;\n  }\n\n  /**\n   * （暂未支持）回复人昵称\n   */\n  public String getInReplyToScreenName() {\n    return inReplyToScreenName;\n  }\n\n  public void setInReplyToScreenName(String inReplyToScreenName) {\n    this.inReplyToScreenName = inReplyToScreenName;\n  }\n\n  /**\n   * 缩略图片地址，没有时不返回此字段\n   */\n  public String getThumbnailPic() {\n    return thumbnailPic;\n  }\n\n  public void setThumbnailPic(String thumbnailPic) {\n    this.thumbnailPic = thumbnailPic;\n  }\n\n  /**\n   * 中等尺寸图片地址，没有时不返回此字段\n   */\n  public String getBmiddlePic() {\n    return bmiddlePic;\n  }\n\n  public void setBmiddlePic(String bmiddlePic) {\n    this.bmiddlePic = bmiddlePic;\n  }\n\n  /**\n   * 原始图片地址，没有时不返回此字段\n   */\n  public String getOriginalPic() {\n    return originalPic;\n  }\n\n  public void setOriginalPic(String originalPic) {\n    this.originalPic = originalPic;\n  }\n\n  /**\n   * 地理信息字段\n   */\n  public Geo getGeo() {\n    return geo;\n  }\n\n  public void setGeo(Geo geo) {\n    this.geo = geo;\n  }\n\n  /**\n   * 微博作者的用户信息字段\n   */\n  public User getUser() {\n    return user;\n  }\n\n  public void setUser(User user) {\n    this.user = user;\n  }\n\n  /**\n   * 被转发的原微博信息字段，当该微博为转发微博时返回\n   */\n  public Status getRetweetedStatus() {\n    return retweetedStatus;\n  }\n\n  public void setRetweetedStatus(Status retweetedStatus) {\n    this.retweetedStatus = retweetedStatus;\n  }\n\n  /**\n   * 转发数\n   */\n  public Integer getRepostsCount() {\n    return repostsCount;\n  }\n\n  public void setRepostsCount(Integer repostsCount) {\n    this.repostsCount = repostsCount;\n  }\n\n  /**\n   * 评论数\n   */\n  public Integer getCommentsCount() {\n    return commentsCount;\n  }\n\n  public void setCommentsCount(Integer commentsCount) {\n    this.commentsCount = commentsCount;\n  }\n\n  /**\n   * 表态数\n   */\n  public Integer getAttitudesCount() {\n    return attitudesCount;\n  }\n\n  public void setAttitudesCount(Integer attitudesCount) {\n    this.attitudesCount = attitudesCount;\n  }\n\n  /**\n   * 暂未支持\n   */\n  public Integer getMlevel() {\n    return mlevel;\n  }\n\n  public void setMlevel(Integer mlevel) {\n    this.mlevel = mlevel;\n  }\n\n  /**\n   * 微博的可见性及指定可见分组信息\n   */\n  public Visible getVisible() {\n    return visible;\n  }\n\n  public void setVisible(Visible visible) {\n    this.visible = visible;\n  }\n\n  /**\n   * 微博配图地址。多图时返回多图链接。无配图返回“[]”\n   */\n  public List<String> getPicUrls() {\n    return picUrls;\n  }\n\n  public void setPicUrls(List<String> picUrls) {\n    this.picUrls = picUrls;\n  }\n\n  /**\n   * 微博流内的推广微博ID\n   */\n  public List<String> getAd() {\n    return ad;\n  }\n\n  public void setAd(List<String> ad) {\n    this.ad = ad;\n  }\n\n  public static Status parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Status obj = new Status(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.mid = Result.toString(jsonObject.opt(\"mid\"));\n    obj.idstr = Result.toString(jsonObject.opt(\"idstr\"));\n    obj.createdAt =\n        Result\n            .parseDate(jsonObject.opt(\"created_at\"), \"EEE MMM dd HH:mm:ss Z yyyy\", Locale.ENGLISH);\n    obj.text = Result.toString(jsonObject.get(\"text\"));\n    obj.source = Result.toString(jsonObject.opt(\"source\"));\n    obj.favorited = Result.parseBoolean(jsonObject.opt(\"favorited\"));\n    obj.truncated = Result.parseBoolean(jsonObject.opt(\"truncated\"));\n    obj.inReplyToStatusId = Result.toString(jsonObject.opt(\"in_reply_to_status_id\"));\n    obj.inReplyToUserId = Result.toString(jsonObject.opt(\"in_reply_to_user_id\"));\n    obj.inReplyToScreenName = Result.toString(jsonObject.opt(\"in_reply_to_screen_name\"));\n    obj.thumbnailPic = Result.toString(jsonObject.opt(\"thumbnail_pic\"));\n    obj.bmiddlePic = Result.toString(jsonObject.opt(\"bmiddle_pic\"));\n    obj.originalPic = Result.toString(jsonObject.opt(\"original_pic\"));\n    obj.geo = Geo.parse(jsonObject.optJSONObject(\"geo\"));\n    obj.user = User.parse(jsonObject.optJSONObject(\"user\"));\n    obj.retweetedStatus = Status.parse(jsonObject.optJSONObject(\"retweeted_status\"));\n    obj.repostsCount = Result.parseInteger(jsonObject.opt(\"reposts_count\"));\n    obj.commentsCount = Result.parseInteger(jsonObject.opt(\"comments_count\"));\n    obj.attitudesCount = Result.parseInteger(jsonObject.opt(\"attitudes_count\"));\n    obj.mlevel = Result.parseInteger(jsonObject.opt(\"mlevel\"));\n    obj.visible = Visible.parse(jsonObject.optJSONObject(\"visible\"));\n    obj.picUrls = Result.parse(jsonObject.optJSONArray(\"pic_urls\"), String.class);\n    obj.ad = Result.parse(jsonObject.optJSONArray(\"ad\"), String.class);\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/TokenInfo.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\npublic class TokenInfo extends JsonBean {\n\n  public TokenInfo() {}\n\n  private TokenInfo(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String uid;\n  private String appkey;// access_token所属的应用appkey。\n  private String scope;// 用户授权的scope权限。\n  private Long createAt;// access_token的创建时间，从1970年到创建时间的秒数。\n  private Long expireIn;// access_token的剩余时间，单位是秒数。\n\n  /**\n   * 授权用户的uid。\n   */\n  public String getUid() {\n    return uid;\n  }\n\n  public void setUid(String uid) {\n    this.uid = uid;\n  }\n\n  /**\n   * access_token所属的应用appkey。\n   */\n  public String getAppkey() {\n    return appkey;\n  }\n\n  public void setAppkey(String appkey) {\n    this.appkey = appkey;\n  }\n\n  /**\n   * 用户授权的scope权限。\n   */\n  public String getScope() {\n    return scope;\n  }\n\n  public void setScope(String scope) {\n    this.scope = scope;\n  }\n\n  /**\n   * access_token的创建时间，从1970年到创建时间的秒数。\n   */\n  public Long getCreateAt() {\n    return createAt;\n  }\n\n  public void setCreateAt(Long createAt) {\n    this.createAt = createAt;\n  }\n\n\n  /**\n   * access_token的剩余时间，单位是秒数。\n   */\n  public Long getExpireIn() {\n    return expireIn;\n  }\n\n  public void setExpireIn(Long expireIn) {\n    this.expireIn = expireIn;\n  }\n\n  public static TokenInfo parse(JSONObject jsonObject) {\n    TokenInfo obj = new TokenInfo(jsonObject);\n    obj.uid = Result.toString(jsonObject.get(\"uid\"));\n    obj.appkey = Result.toString(jsonObject.opt(\"appkey\"));\n    obj.scope = Result.toString(jsonObject.opt(\"scope\"));\n    obj.createAt = Result.parseLong(jsonObject.opt(\"create_at\"));\n    obj.expireIn = Result.parseLong(jsonObject.opt(\"expire_in\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/UrlShort.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 短链\n */\npublic class UrlShort extends JsonBean {\n\n  public UrlShort() {}\n\n  private UrlShort(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String urlShort;// 短链接\n  private String urlLong;// 原始长链接\n  private Integer type;// 链接的类型，0：普通网页、1：视频、2：音乐、3：活动、5、投票\n  private Boolean result;// 短链的可用状态，true：可用、false：不可用。\n\n  /**\n   * 短链接\n   */\n  public String getUrlShort() {\n    return urlShort;\n  }\n\n  public void setUrlShort(String urlShort) {\n    this.urlShort = urlShort;\n  }\n\n  /**\n   * 原始长链接\n   */\n  public String getUrlLong() {\n    return urlLong;\n  }\n\n  public void setUrlLong(String urlLong) {\n    this.urlLong = urlLong;\n  }\n\n  /**\n   * 链接的类型，0：普通网页、1：视频、2：音乐、3：活动、5、投票\n   */\n  public Integer getType() {\n    return type;\n  }\n\n  public void setType(Integer type) {\n    this.type = type;\n  }\n\n  /**\n   * 短链的可用状态，true：可用、false：不可用。\n   */\n  public Boolean getResult() {\n    return result;\n  }\n\n  public void setResult(Boolean result) {\n    this.result = result;\n  }\n\n  public static UrlShort parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    UrlShort obj = new UrlShort(jsonObject);\n    obj.urlShort = Result.toString(jsonObject.opt(\"url_short\"));\n    obj.urlLong = Result.toString(jsonObject.opt(\"url_long\"));\n    obj.type = Result.parseInteger(jsonObject.opt(\"type\"));\n    obj.result = Result.parseBoolean(jsonObject.opt(\"result\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/User.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport java.util.Date;\nimport java.util.Locale;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Gender;\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.OnlineStatus;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 用户\n * \n * 文档地址：http://open.weibo.com/wiki/常见返回对象数据结构#.E7.94.A8.E6.88.B7.EF.BC.88user.EF.BC.89\n */\npublic class User extends JsonBean {\n\n  public User() {}\n\n  private User(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 用户UID\n  private String idstr;// 字符串型的用户UID\n  private String screenName;// 用户昵称\n  private String name;// screenName\n  private Integer province;// 用户所在省级ID\n  private Integer city;// 用户所在城市ID\n  private String location;// 用户所在地\n  private String description;// 用户个人描述\n  private String url;// 用户博客地址\n  private String profileImageUrl;// 用户头像地址，50×50像素\n  private String profileUrl;// 用户的微博统一URL地址\n  private String domain;// 用户的个性化域名\n  private String weihao;// 用户的微号\n  private Gender gender;// 性别\n  private Integer followersCount;// 粉丝数\n  private Integer friendsCount;// 关注数\n  private Integer statusesCount;// 微博数\n  private Integer favouritesCount;// 收藏数\n  private Date createdAt;// 用户创建（注册）时间\n  private Boolean following;// 暂未支持\n  private Boolean allowAllActMsg;// 是否允许所有人给我发私信\n  private Boolean geoEnabled;// 是否允许标识用户的地理位置\n  private Boolean verified;// 是否是微博认证用户，即加V用户\n  private Integer verifiedType;// 暂未支持\n  private String remark;// 用户备注信息，只有在查询用户关系时才返回此字段\n  private Status status;// 用户的最近一条微博信息字段 详细\n  private Boolean allowAllComment;// 是否允许所有人对我的微博进行评论\n  private String avatarLarge;// 用户大头像地址\n  private String verifiedReason;// 认证原因\n  private Boolean followMe;// 该用户是否关注当前登录用户\n  private OnlineStatus onlineStatus;// 用户的在线状态\n  private Integer biFollowersCount;// 用户的互粉数\n  private String lang;// 用户当前的语言版本，zh-cn：简体中文，zh-tw：繁体中文，en：英语\n\n  /**\n   * 用户UID\n   */\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  /**\n   * 字符串型的用户UID\n   */\n  public String getIdstr() {\n    return idstr;\n  }\n\n  public void setIdstr(String idstr) {\n    this.idstr = idstr;\n  }\n\n  /**\n   * 用户昵称\n   */\n  public String getScreenName() {\n    return screenName;\n  }\n\n  public void setScreenName(String screenName) {\n    this.screenName = screenName;\n  }\n\n  /**\n   * 友好显示名称\n   */\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  /**\n   * 用户所在省级ID\n   */\n  public Integer getProvince() {\n    return province;\n  }\n\n  public void setProvince(Integer province) {\n    this.province = province;\n  }\n\n  /**\n   * 用户所在城市ID\n   */\n  public Integer getCity() {\n    return city;\n  }\n\n  public void setCity(Integer city) {\n    this.city = city;\n  }\n\n  /**\n   * 用户所在地\n   */\n  public String getLocation() {\n    return location;\n  }\n\n  public void setLocation(String location) {\n    this.location = location;\n  }\n\n  /**\n   * 用户个人描述\n   */\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  /**\n   * 用户博客地址\n   */\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  /**\n   * 用户头像地址，50×50像素\n   */\n  public String getProfileImageUrl() {\n    return profileImageUrl;\n  }\n\n  public void setProfileImageUrl(String profileImageUrl) {\n    this.profileImageUrl = profileImageUrl;\n  }\n\n  /**\n   * 用户的微博统一URL地址\n   */\n  public String getProfileUrl() {\n    return profileUrl;\n  }\n\n  public void setProfileUrl(String profileUrl) {\n    this.profileUrl = profileUrl;\n  }\n\n  /**\n   * 用户的个性化域名\n   */\n  public String getDomain() {\n    return domain;\n  }\n\n  public void setDomain(String domain) {\n    this.domain = domain;\n  }\n\n  /**\n   * 用户的微号\n   */\n  public String getWeihao() {\n    return weihao;\n  }\n\n  public void setWeihao(String weihao) {\n    this.weihao = weihao;\n  }\n\n  /**\n   * 性别\n   */\n  public Gender getGender() {\n    return gender;\n  }\n\n  public void setGender(Gender gender) {\n    this.gender = gender;\n  }\n\n  /**\n   * 粉丝数\n   */\n  public Integer getFollowersCount() {\n    return followersCount;\n  }\n\n  public void setFollowersCount(Integer followersCount) {\n    this.followersCount = followersCount;\n  }\n\n  /**\n   * 关注数\n   */\n  public Integer getFriendsCount() {\n    return friendsCount;\n  }\n\n  public void setFriendsCount(Integer friendsCount) {\n    this.friendsCount = friendsCount;\n  }\n\n  /**\n   * 微博数\n   */\n  public Integer getStatusesCount() {\n    return statusesCount;\n  }\n\n  public void setStatusesCount(Integer statusesCount) {\n    this.statusesCount = statusesCount;\n  }\n\n  /**\n   * 收藏数\n   */\n  public Integer getFavouritesCount() {\n    return favouritesCount;\n  }\n\n  public void setFavouritesCount(Integer favouritesCount) {\n    this.favouritesCount = favouritesCount;\n  }\n\n  /**\n   * 用户创建（注册）时间\n   */\n  public Date getCreatedAt() {\n    return createdAt;\n  }\n\n  public void setCreatedAt(Date createdAt) {\n    this.createdAt = createdAt;\n  }\n\n  /**\n   * 暂未支持\n   */\n  public Boolean getFollowing() {\n    return following;\n  }\n\n  public void setFollowing(Boolean following) {\n    this.following = following;\n  }\n\n  /**\n   * 是否允许所有人给我发私信\n   */\n  public Boolean getAllowAllActMsg() {\n    return allowAllActMsg;\n  }\n\n  public void setAllowAllActMsg(Boolean allowAllActMsg) {\n    this.allowAllActMsg = allowAllActMsg;\n  }\n\n  /**\n   * 是否允许标识用户的地理位置\n   */\n  public Boolean getGeoEnabled() {\n    return geoEnabled;\n  }\n\n  public void setGeoEnabled(Boolean geoEnabled) {\n    this.geoEnabled = geoEnabled;\n  }\n\n  /**\n   * 是否是微博认证用户，即加V用户\n   */\n  public Boolean getVerified() {\n    return verified;\n  }\n\n  public void setVerified(Boolean verified) {\n    this.verified = verified;\n  }\n\n  /**\n   * 暂未支持\n   */\n  public Integer getVerifiedType() {\n    return verifiedType;\n  }\n\n  public void setVerifiedType(Integer verifiedType) {\n    this.verifiedType = verifiedType;\n  }\n\n  /**\n   * 用户备注信息，只有在查询用户关系时才返回此字段\n   */\n  public String getRemark() {\n    return remark;\n  }\n\n  public void setRemark(String remark) {\n    this.remark = remark;\n  }\n\n  /**\n   * 用户的最近一条微博信息字段 详细\n   */\n  public Status getStatus() {\n    return status;\n  }\n\n  public void setStatus(Status status) {\n    this.status = status;\n  }\n\n  /**\n   * 是否允许所有人对我的微博进行评论\n   */\n  public Boolean getAllowAllComment() {\n    return allowAllComment;\n  }\n\n  public void setAllowAllComment(Boolean allowAllComment) {\n    this.allowAllComment = allowAllComment;\n  }\n\n  /**\n   * 用户大头像地址\n   */\n  public String getAvatarLarge() {\n    return avatarLarge;\n  }\n\n  public void setAvatarLarge(String avatarLarge) {\n    this.avatarLarge = avatarLarge;\n  }\n\n  /**\n   * 认证原因\n   */\n  public String getVerifiedReason() {\n    return verifiedReason;\n  }\n\n  public void setVerifiedReason(String verifiedReason) {\n    this.verifiedReason = verifiedReason;\n  }\n\n  /**\n   * 该用户是否关注当前登录用户\n   */\n  public Boolean getFollowMe() {\n    return followMe;\n  }\n\n  public void setFollowMe(Boolean followMe) {\n    this.followMe = followMe;\n  }\n\n  /**\n   * 用户的在线状态\n   */\n  public OnlineStatus getOnlineStatus() {\n    return onlineStatus;\n  }\n\n  public void setOnlineStatus(OnlineStatus onlineStatus) {\n    this.onlineStatus = onlineStatus;\n  }\n\n  /**\n   * 用户的互粉数\n   */\n  public Integer getBiFollowersCount() {\n    return biFollowersCount;\n  }\n\n  public void setBiFollowersCount(Integer biFollowersCount) {\n    this.biFollowersCount = biFollowersCount;\n  }\n\n  /**\n   * 用户当前的语言版本，zh-cn：简体中文，zh-tw：繁体中文，en：英语\n   */\n  public String getLang() {\n    return lang;\n  }\n\n  public void setLang(String lang) {\n    this.lang = lang;\n  }\n\n  public static User parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    User obj = new User(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.idstr = Result.toString(jsonObject.opt(\"idstr\"));\n    obj.screenName = Result.toString(jsonObject.opt(\"screen_name\"));\n    obj.name = Result.toString(jsonObject.opt(\"name\"));\n    obj.province = Result.parseInteger(jsonObject.opt(\"province\"));\n    obj.city = Result.parseInteger(jsonObject.opt(\"city\"));\n    obj.location = Result.toString(jsonObject.opt(\"location\"));\n    obj.description = Result.toString(jsonObject.opt(\"description\"));\n    obj.url = Result.toString(jsonObject.opt(\"url\"));\n    obj.profileImageUrl = Result.toString(jsonObject.opt(\"profile_image_url\"));\n    obj.profileUrl = Result.toString(jsonObject.opt(\"profile_url\"));\n    obj.domain = Result.toString(jsonObject.opt(\"domain\"));\n    obj.weihao = Result.toString(jsonObject.opt(\"weihao\"));\n    obj.gender = Gender.parse(jsonObject.optString(\"gender\", null));\n    obj.followersCount = Result.parseInteger(jsonObject.opt(\"followers_count\"));\n    obj.friendsCount = Result.parseInteger(jsonObject.opt(\"friends_count\"));\n    obj.statusesCount = Result.parseInteger(jsonObject.opt(\"statuses_count\"));\n    obj.favouritesCount = Result.parseInteger(jsonObject.opt(\"favourites_count\"));\n    obj.createdAt =\n        Result\n            .parseDate(jsonObject.opt(\"created_at\"), \"EEE MMM dd HH:mm:ss Z yyyy\", Locale.ENGLISH);\n    obj.following = Result.parseBoolean(jsonObject.opt(\"following\"));\n    obj.allowAllActMsg = Result.parseBoolean(jsonObject.opt(\"allow_all_act_msg\"));\n    obj.geoEnabled = Result.parseBoolean(jsonObject.opt(\"geo_enabled\"));\n    obj.verified = Result.parseBoolean(jsonObject.opt(\"verified\"));\n    obj.verifiedType = Result.parseInteger(jsonObject.opt(\"verified_type\"));\n    obj.remark = Result.toString(jsonObject.opt(\"remark\"));\n    obj.status = Status.parse(jsonObject.optJSONObject(\"status\"));\n    obj.allowAllComment = Result.parseBoolean(jsonObject.opt(\"allow_all_comment\"));\n    obj.avatarLarge = Result.toString(jsonObject.opt(\"avatar_large\"));\n    obj.verifiedReason = Result.toString(jsonObject.opt(\"verified_reason\"));\n    obj.followMe = Result.parseBoolean(jsonObject.opt(\"follow_me\"));\n    obj.onlineStatus =\n        OnlineStatus.parse(Result.parseInteger(jsonObject.optString(\"online_status\")));\n    obj.biFollowersCount = Result.parseInteger(jsonObject.opt(\"bi_followers_count\"));\n    obj.lang = Result.toString(jsonObject.opt(\"lang\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/UserCounts.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 用户的粉丝数、关注数、微博数\n */\npublic class UserCounts extends JsonBean {\n\n  public UserCounts() {}\n\n  private UserCounts(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 微博ID\n  private Integer followersCount;// 粉丝数\n  private Integer friendsCount;// 关注数\n  private Integer statusesCount;// 微博数\n  private Integer privateFriendsCount;// 暂未支持\n\n  /**\n   * 微博ID\n   */\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  /**\n   * 粉丝数\n   */\n  public Integer getFollowersCount() {\n    return followersCount;\n  }\n\n  public void setFollowersCount(Integer followersCount) {\n    this.followersCount = followersCount;\n  }\n\n  /**\n   * 关注数\n   */\n  public Integer getFriendsCount() {\n    return friendsCount;\n  }\n\n  public void setFriendsCount(Integer friendsCount) {\n    this.friendsCount = friendsCount;\n  }\n\n  /**\n   * 微博数\n   */\n  public Integer getStatusesCount() {\n    return statusesCount;\n  }\n\n  public void setStatusesCount(Integer statusesCount) {\n    this.statusesCount = statusesCount;\n  }\n\n  /**\n   * 暂未支持\n   */\n  public Integer getPrivateFriendsCount() {\n    return privateFriendsCount;\n  }\n\n  public void setPrivateFriendsCount(Integer privateFriendsCount) {\n    this.privateFriendsCount = privateFriendsCount;\n  }\n\n\n  public static UserCounts parse(JSONObject jsonObject) {\n    UserCounts obj = new UserCounts(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.followersCount = Result.parseInteger(jsonObject.opt(\"followers_count\"));\n    obj.friendsCount = Result.parseInteger(jsonObject.opt(\"friends_count\"));\n    obj.statusesCount = Result.parseInteger(jsonObject.opt(\"statuses_count\"));\n    obj.privateFriendsCount = Result.parseInteger(jsonObject.opt(\"private_friends_count\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weibo/bean/Visible.java",
    "content": "package com.belerweb.social.weibo.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 微博的可见性及指定可见分组信息\n */\npublic class Visible extends JsonBean {\n\n  public Visible() {}\n\n  private Visible(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Integer type;// 0：普通微博，1：私密微博，3：指定分组微博，4：密友微博\n  private Integer listId;// 分组的组号\n\n  /**\n   * 0：普通微博，1：私密微博，3：指定分组微博，4：密友微博\n   */\n  public Integer getType() {\n    return type;\n  }\n\n  public void setType(Integer type) {\n    this.type = type;\n  }\n\n  /**\n   * 分组的组号\n   */\n  public Integer getListId() {\n    return listId;\n  }\n\n  public void setListId(Integer listId) {\n    this.listId = listId;\n  }\n\n  public static Visible parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Visible obj = new Visible(jsonObject);\n    obj.type = Result.parseInteger(jsonObject.opt(\"type\"));\n    obj.listId = Result.parseInteger(jsonObject.opt(\"list_id\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/Group.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.http.NameValuePair;\nimport org.apache.http.entity.StringEntity;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\n\n/**\n * 分组管理接口\n * \n * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=分组管理接口\n */\npublic class Group extends API {\n\n  protected Group(Weixin weixin) {\n    super(weixin);\n  }\n\n  /**\n   * 查询分组\n   */\n  public Result<List<com.belerweb.social.weixin.bean.Group>> get() {\n    return get(weixin.getAccessToken().getToken());\n  }\n\n  /**\n   * 查询分组\n   * \n   * @param accessToken 调用接口凭证\n   */\n  public Result<List<com.belerweb.social.weixin.bean.Group>> get(String accessToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    String json = weixin.get(\"https://api.weixin.qq.com/cgi-bin/groups/get\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error == null) {\n      List<com.belerweb.social.weixin.bean.Group> groups =\n          Result.parse(jsonObject.getJSONArray(\"groups\"),\n              com.belerweb.social.weixin.bean.Group.class);\n      return new Result<List<com.belerweb.social.weixin.bean.Group>>(groups);\n    }\n    return new Result<List<com.belerweb.social.weixin.bean.Group>>(error);\n  }\n\n  /**\n   * 创建分组\n   * \n   * @param name 分组名字（30个字符以内）\n   */\n  public Result<com.belerweb.social.weixin.bean.Group> create(String name) {\n    return create(weixin.getAccessToken().getToken(), name);\n  }\n\n  /**\n   * 创建分组\n   * \n   * 一个公众账号，最多支持创建500个分组。\n   * \n   * @param accessToken 调用接口凭证\n   * @param name 分组名字（30个字符以内）\n   */\n  public Result<com.belerweb.social.weixin.bean.Group> create(String accessToken, String name) {\n    JSONObject request = new JSONObject();\n    JSONObject group = new JSONObject();\n    group.put(\"name\", name);\n    request.put(\"group\", group);\n    try {\n      String json =\n          weixin.post(\n              \"https://api.weixin.qq.com/cgi-bin/groups/create?access_token=\" + accessToken,\n              new StringEntity(request.toString()));\n      JSONObject jsonObject = new JSONObject(json);\n      Error error = Error.parse(jsonObject);\n      if (error != null) {\n        return new Result<com.belerweb.social.weixin.bean.Group>(error);\n      }\n      return new Result<com.belerweb.social.weixin.bean.Group>(\n          com.belerweb.social.weixin.bean.Group.parse(jsonObject.getJSONObject(\"group\")));\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 修改分组名\n   * \n   * @param id 分组id，由微信分配\n   * @param name 分组名字（30个字符以内）\n   */\n  public Result<Error> update(String id, String name) {\n    return update(weixin.getAccessToken().getToken(), id, name);\n  }\n\n  /**\n   * 修改分组名\n   * \n   * @param accessToken 调用接口凭证\n   * @param id 分组id，由微信分配\n   * @param name 分组名字（30个字符以内）\n   */\n  public Result<Error> update(String accessToken, String id, String name) {\n    JSONObject request = new JSONObject();\n    JSONObject group = new JSONObject();\n    group.put(\"id\", id);\n    group.put(\"name\", name);\n    request.put(\"group\", group);\n    try {\n      String json =\n          weixin.post(\n              \"https://api.weixin.qq.com/cgi-bin/groups/update?access_token=\" + accessToken,\n              new StringEntity(request.toString()));\n      return Result.parse(json, Error.class);\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 移动用户分组\n   * \n   * @param openId 用户唯一标识符\n   * @param groupId 分组id\n   */\n  public Result<Error> move(String openId, String groupId) {\n    return move(weixin.getAccessToken().getToken(), openId, groupId);\n  }\n\n  /**\n   * 移动用户分组\n   * \n   * @param accessToken 调用接口凭证\n   * @param openId 用户唯一标识符\n   * @param groupId 分组id\n   */\n  public Result<Error> move(String accessToken, String openId, String groupId) {\n    JSONObject request = new JSONObject();\n    request.put(\"openid\", openId);\n    request.put(\"to_groupid\", groupId);\n    try {\n      String json =\n          weixin.post(\"https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token=\"\n              + accessToken, new StringEntity(request.toString()));\n      return Result.parse(json, Error.class);\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/Media.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.IOException;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.http.Header;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.entity.ContentType;\nimport org.apache.http.entity.mime.MultipartEntityBuilder;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.http.Http;\nimport com.belerweb.social.weixin.bean.MediaType;\n\n/**\n * 上传下载多媒体文件\n * \n * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件\n * \n * 公众号在使用接口时，对多媒体文件、多媒体消息的获取和调用等操作，是通过media_id来进行的。通过本接口，公众号可以上传或下载多媒体文件。但请注意，每个多媒体文件（media_id）会在上传、\n * 用户发送到微信服务器3天后自动删除，以节省服务器资源。\n * \n */\npublic class Media extends API {\n\n  protected Media(Weixin weixin) {\n    super(weixin);\n  }\n\n  /**\n   * 上传多媒体文件，将把上传成功后的mediaId设置回传入的media中\n   * \n   * @param type 媒体文件类型\n   * @param media form-data中媒体文件标识，有filename、filelength、content-type等信息\n   */\n  public Result<com.belerweb.social.weixin.bean.Media> upload(MediaType type,\n      com.belerweb.social.weixin.bean.Media media) {\n    return upload(weixin.getAccessToken().getToken(), type, media);\n  }\n\n  /**\n   * 上传多媒体文件，将把上传成功后的mediaId设置回传入的media中\n   * \n   * 图片（image）: 256K，支持JPG格式\n   * \n   * 语音（voice）：256K，播放长度不超过60s，支持AMR与MP3格式\n   * \n   * 视频（video）：2MB，支持MP4格式\n   * \n   * 缩略图（thumb）：64KB，支持JPG格式\n   * \n   * @param accessToken 调用接口凭证\n   * @param type 媒体文件类型\n   * @param media form-data中媒体文件标识，有filename、filelength、content-type等信息\n   */\n  public Result<com.belerweb.social.weixin.bean.Media> upload(String accessToken, MediaType type,\n      com.belerweb.social.weixin.bean.Media media) {\n    String url =\n        \"http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=\" + accessToken + \"&type=\"\n            + type.value();\n    HttpPost request = new HttpPost(url);\n    MultipartEntityBuilder builder =\n        MultipartEntityBuilder.create().addBinaryBody(\"media\", media.getContent(),\n            ContentType.create(media.getContentType()), media.getName());\n    request.setEntity(builder.build());\n    try {\n      HttpResponse response = Http.CLIENT.execute(request);\n      String json = IOUtils.toString(response.getEntity().getContent());\n      JSONObject jsonObject = new JSONObject(json);\n      Error error = Error.parse(jsonObject);\n      if (error != null) {\n        return new Result<com.belerweb.social.weixin.bean.Media>(error);\n      }\n      media.setId(jsonObject.getString(\"media_id\"));\n      return new Result<com.belerweb.social.weixin.bean.Media>(media);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 下载多媒体文件\n   * \n   * @param mediaId 媒体文件ID\n   */\n  public Result<com.belerweb.social.weixin.bean.Media> get(String mediaId) {\n    return get(weixin.getAccessToken().getToken(), mediaId);\n  }\n\n  /**\n   * 下载多媒体文件\n   * \n   * 公众号可调用本接口来获取多媒体文件。请注意，调用该接口需http协议。\n   * \n   * @param accessToken 调用接口凭证\n   * @param mediaId 媒体文件ID\n   */\n  public Result<com.belerweb.social.weixin.bean.Media> get(String accessToken, String mediaId) {\n    String url =\n        \"http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=\" + accessToken\n            + \"&media_id=\" + mediaId;\n    try {\n      HttpResponse response = Http.CLIENT.execute(new HttpGet(url));\n      Header disposition = response.getFirstHeader(\"Content-disposition\");\n      HttpEntity entity = response.getEntity();\n      if (disposition == null) {\n        return new Result<com.belerweb.social.weixin.bean.Media>(Error.parse(new JSONObject(IOUtils\n            .toString(entity.getContent()))));\n      }\n\n      String fileName = disposition.getValue();\n      fileName = fileName.substring(fileName.indexOf(\"\\\"\") + 1, fileName.lastIndexOf(\"\\\"\"));\n      com.belerweb.social.weixin.bean.Media media = new com.belerweb.social.weixin.bean.Media();\n      media.setId(mediaId);\n      media.setName(fileName);\n      media.setContentType(entity.getContentType().getValue());\n      media.setContent(IOUtils.toByteArray(entity.getContent()));\n      return new Result<com.belerweb.social.weixin.bean.Media>(media);\n    } catch (ClientProtocolException e) {\n      throw new SocialException(e);\n    } catch (IOException e) {\n      throw new SocialException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/Menu.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.http.NameValuePair;\nimport org.apache.http.entity.StringEntity;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.weixin.bean.MenuType;\n\n/**\n * 自定义菜单接口\n * \n * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=自定义菜单创建接口\n */\npublic class Menu extends API {\n\n  protected Menu(Weixin weixin) {\n    super(weixin);\n  }\n\n  /**\n   * 自定义菜单创建接口\n   * \n   * @param menus 菜单\n   */\n  public Result<Error> create(List<com.belerweb.social.weixin.bean.Menu> menus) {\n    return create(weixin.getAccessToken().getToken(), menus);\n  }\n\n  /**\n   * 自定义菜单创建接口\n   * <p>\n   * 注意：只有菜单类型为 MenuType.VIEW 时才需要url属性，其它情况都是使用key\n   * </p>\n   * \n   * @param accessToken 调用接口凭证\n   * @param menus 菜单\n   */\n  public Result<Error> create(String accessToken, List<com.belerweb.social.weixin.bean.Menu> menus) {\n    JSONArray menuArray = new JSONArray();\n    for (com.belerweb.social.weixin.bean.Menu menu : menus) {\n      JSONObject obj = new JSONObject();\n      MenuType type = menu.getType();\n      obj.put(\"name\", menu.getName());\n      if (type != null) {\n        obj.put(\"type\", type.value());\n        if (type == MenuType.VIEW) {\n          obj.put(\"url\", menu.getUrl());\n        } else {\n          obj.put(\"key\", menu.getKey());\n        }\n      }\n      List<com.belerweb.social.weixin.bean.Menu> subs = menu.getSubs();\n      if (subs != null) {\n        JSONArray _menuArray = new JSONArray();\n        for (com.belerweb.social.weixin.bean.Menu _menu : subs) {\n          JSONObject _obj = new JSONObject();\n          MenuType _type = _menu.getType();\n          _obj.put(\"name\", _menu.getName());\n          if (_type != null) {\n            _obj.put(\"type\", _type.value());\n            if (_type == MenuType.VIEW) {\n              _obj.put(\"url\", _menu.getUrl());\n            } else {\n              _obj.put(\"key\", _menu.getKey());\n            }\n          }\n          _menuArray.put(_obj);\n        }\n        obj.put(\"sub_button\", _menuArray);\n      }\n      menuArray.put(obj);\n    }\n    JSONObject request = new JSONObject();\n    request.put(\"button\", menuArray);\n    try {\n      String json =\n          weixin.post(\"https://api.weixin.qq.com/cgi-bin/menu/create?access_token=\" + accessToken,\n              new StringEntity(request.toString(), \"UTF-8\"));\n      return Result.parse(json, Error.class);\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 自定义菜单查询接口\n   */\n  public Result<List<com.belerweb.social.weixin.bean.Menu>> get() {\n    return get(weixin.getAccessToken().getToken());\n  }\n\n  /**\n   * 自定义菜单查询接口\n   * \n   * @param accessToken 调用接口凭证\n   */\n  public Result<List<com.belerweb.social.weixin.bean.Menu>> get(String accessToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    String json = weixin.get(\"https://api.weixin.qq.com/cgi-bin/menu/get\", params);\n    JSONObject jsonObject = new JSONObject(json);\n    Error error = Error.parse(jsonObject);\n    if (error != null) {\n      return new Result<List<com.belerweb.social.weixin.bean.Menu>>(error);\n    }\n    List<com.belerweb.social.weixin.bean.Menu> menus =\n        new ArrayList<com.belerweb.social.weixin.bean.Menu>();\n    JSONObject menu = jsonObject.optJSONObject(\"menu\");\n    if (menu != null) {\n      menus = Result.parse(menu.optJSONArray(\"button\"), com.belerweb.social.weixin.bean.Menu.class);\n    }\n    return new Result<List<com.belerweb.social.weixin.bean.Menu>>(menus);\n  }\n\n  /**\n   * 自定义菜单删除接口\n   */\n  public Result<Error> delete() {\n    return delete(weixin.getAccessToken().getToken());\n  }\n\n  /**\n   * 自定义菜单删除接口\n   * \n   * @param accessToken 调用接口凭证\n   */\n  public Result<Error> delete(String accessToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    String json = weixin.get(\"https://api.weixin.qq.com/cgi-bin/menu/delete\", params);\n    return Result.parse(json, Error.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/OAuth2.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.AccessToken;\nimport com.belerweb.social.weixin.bean.Scope;\n\npublic final class OAuth2 extends API {\n\n  OAuth2(Weixin weixin) {\n    super(weixin);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link Weixin} 从获取clientId，redirectUri，responseType为code ，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize() {\n    return authorize(false);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link Weixin} 从获取clientId，redirectUri，responseType为code ，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize(Boolean wechatRedirect) {\n    return authorize(weixin.getRedirectUri(), wechatRedirect);\n  }\n\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link Weixin} 从获取clientId，responseType为code ，scope为snsapi_userinfo，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize(String redirectUri) {\n    return authorize(redirectUri, false);\n  }\n\n\n  /**\n   * 获取Authorization Code\n   * \n   * 从 {@link Weixin} 从获取clientId，responseType为code ，scope为snsapi_userinfo，其余参数默认\n   * \n   * @see OAuth2#authorize(String, String, String, String, String, Boolean)\n   */\n  public String authorize(String redirectUri, Boolean wechatRedirect) {\n    return authorize(weixin.getAppId(), redirectUri, \"code\", Scope.SNSAPI_USERINFO, null,\n        wechatRedirect);\n  }\n\n  /**\n   * 获取Authorization Code\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息\n   * \n   * @param appId 必须，公众号的唯一标识\n   * @param redirectUri 必须，授权后重定向的回调链接地址\n   * @param responseType 必须，返回类型，请填写code\n   * @param scope 必须，应用授权作用域\n   * @param state 重定向后会带上state参数，开发者可以填写任意参数值\n   * @param wechatRedirect 直接在微信打开链接，可以不填此参数。做页面302重定向时候，必须带此参数\n   */\n  public String authorize(String appId, String redirectUri, String responseType, Scope scope,\n      String state, Boolean wechatRedirect) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"appid\", appId);\n    weixin.addParameter(params, \"redirect_uri\", redirectUri);\n    weixin.addParameter(params, \"response_type\", responseType);\n    weixin.addParameter(params, \"scope\", scope);\n    weixin.addNotNullParameter(params, \"state\", state);\n    String result =\n        \"https://open.weixin.qq.com/connect/oauth2/authorize?\" + StringUtils.join(params, \"&\");\n    if (Boolean.TRUE.equals(wechatRedirect)) {\n      result = result + \"#wechat_redirect\";\n    }\n    return result;\n  }\n\n  /**\n   * 获取网站应用登录的Authorization Code，调用顺序如下：\n   * \n   * <pre>\n   *    Weixin weixin = new Weixin(weixinAppId, weixinAppSecret);\n   *    weixin.setRedirectUri(weixinAppRedirect);\n   *    weixin.getOAuth2().authorizeLogin(state);        \n   * </pre>\n   * \n   * <p>\n   * 文档地址：https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify\n   * =1&id=open1419316505&token=&lang=zh_CN\n   * \n   * @param state 重定向后会带上state参数，开发者可以填写任意参数值\n   */\n  public String authorizeLogin(String state) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"appid\", weixin.getAppId());\n    String redirect_uri = \"\";\n    try {\n      redirect_uri = URLEncoder.encode(weixin.getRedirectUri(), \"UTF-8\");\n    } catch (UnsupportedEncodingException e) {\n      e.printStackTrace();\n    }\n    weixin.addParameter(params, \"redirect_uri\", redirect_uri);\n    weixin.addParameter(params, \"response_type\", \"code\");\n    weixin.addParameter(params, \"scope\", Scope.SNSAPI_LOGIN);\n    weixin.addNotNullParameter(params, \"state\", state);\n    String result =\n        \"https://open.weixin.qq.com/connect/qrconnect?\" + StringUtils.join(params, \"&\")\n            + \"#wechat_redirect\";\n    return result;\n  }\n\n  /**\n   * 通过code换取网页授权access_token。从{@link Weixin}中获取appId和secret。\n   * \n   * @param code 填写第一步获取的code参数\n   */\n  public Result<AccessToken> accessToken(String code) {\n    return accessToken(weixin.getAppId(), weixin.getSecret(), code);\n  }\n\n\n  /**\n   * 通过code换取网页授权access_token。grantType值为authorization_code。\n   * \n   * @param appId 公众号的唯一标识\n   * @param secret 公众号的appsecret\n   * @param code 填写第一步获取的code参数\n   */\n  public Result<AccessToken> accessToken(String appId, String secret, String code) {\n    return accessToken(appId, secret, code, \"authorization_code\");\n  }\n\n  /**\n   * 通过code换取网页授权access_token\n   * \n   * @param appId 公众号的唯一标识\n   * @param secret 公众号的appsecret\n   * @param code 填写第一步获取的code参数\n   * @param grantType 填写为authorization_code\n   */\n  public Result<AccessToken> accessToken(String appId, String secret, String code, String grantType) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"appid\", appId);\n    weixin.addParameter(params, \"secret\", secret);\n    weixin.addParameter(params, \"code\", code);\n    weixin.addParameter(params, \"grant_type\", grantType);\n    // String result = weixin.post(\"https://api.weixin.qq.com/sns/oauth2/access_token\", params);\n    String result =\n        weixin.get(\"https://api.weixin.qq.com/sns/oauth2/access_token?appid=\" + appId + \"&secret=\"\n            + secret + \"&code=\" + code + \"&grant_type=authorization_code\");\n    return Result.parse(result, AccessToken.class);\n  }\n\n  /**\n   * 刷新access_token（如果需要）。从{@link Weixin}中获取appId\n   * \n   * @param refreshToken 填写通过access_token获取到的refresh_token参数\n   */\n  public Result<AccessToken> refreshAccessToken(String refreshToken) {\n    return refreshAccessToken(weixin.getAppId(), \"refresh_token\", refreshToken);\n  }\n\n  /**\n   * 刷新access_token（如果需要）\n   * \n   * @param appId 公众号的唯一标识\n   * @param refreshToken 填写通过access_token获取到的refresh_token参数\n   */\n  public Result<AccessToken> refreshAccessToken(String appId, String refreshToken) {\n    return refreshAccessToken(appId, \"refresh_token\", refreshToken);\n  }\n\n  /**\n   * 刷新access_token（如果需要）\n   * \n   * @param appId 公众号的唯一标识\n   * @param grantType 填写为refresh_token\n   * @param refreshToken 填写通过access_token获取到的refresh_token参数\n   */\n  public Result<AccessToken> refreshAccessToken(String appId, String grantType, String refreshToken) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"appid\", appId);\n    weixin.addParameter(params, \"grant_type\", grantType);\n    weixin.addParameter(params, \"refresh_token\", refreshToken);\n    String result = weixin.get(\"https://api.weixin.qq.com/sns/oauth2/refresh_token\", params);\n    return Result.parse(result, AccessToken.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/User.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\n\nimport com.belerweb.social.API;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.GetFollowersResult;\n\n/**\n * 网页授权获取用户基本信息\n * \n * 如果用户在微信中（Web微信除外）访问公众号的第三方网页，公众号开发者可以通过此接口获取当前用户基本信息（包括昵称、性别、城市、国家）。利用用户信息，可以实现体验优化、用户来源统计、帐号绑定、\n * 用户身份鉴权等功能\n * 。请注意，“获取用户基本信息接口是在用户和公众号产生消息交互时，才能根据用户OpenID获取用户基本信息，而网页授权的方式获取用户基本信息，则无需消息交互，只是用户进入到公众号的网页\n * ，就可弹出请求用户授权的界面，用户授权后，就可获得其基本信息（此过程甚至不需要用户已经关注公众号。）”\n * \n * 本接口是通过OAuth2.0来完成网页授权的，是安全可靠的，关于OAuth2.0的详细介绍，可以参考OAuth2.0协议标准。在微信公众号请求用户网页授权之前，\n * 开发者需要先到公众平台网站中配置授权回调域名。\n */\npublic class User extends API {\n\n  protected User(Weixin weixin) {\n    super(weixin);\n  }\n\n  /**\n   * 拉取用户信息(需scope为 snsapi_userinfo)。适用于网页授权的用户。\n   * \n   * 如果网页授权作用域为snsapi_userinfo，则此时开发者可以通过access_token和openid拉取用户信息了。\n   * \n   * @param accessToken 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同\n   * @param openId 用户的唯一标识\n   */\n  public Result<com.belerweb.social.weixin.bean.User> snsapiUserInfo(String accessToken,\n      String openId) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    weixin.addParameter(params, \"openid\", openId);\n    String json = weixin.get(\"https://api.weixin.qq.com/sns/userinfo\", params);\n    return Result.parse(json, com.belerweb.social.weixin.bean.User.class);\n  }\n\n  /**\n   * 获取用户基本信息，适用于已关注公众帐号的用户。\n   * \n   * 在关注者与公众号产生消息交互后，公众号可获得关注者的OpenID（加密后的微信号，每个用户对每个公众号的OpenID是唯一的。对于不同公众号，同一用户的openid不同）。\n   * 公众号可通过本接口来根据OpenID获取用户基本信息，包括昵称、头像、性别、所在城市、语言和关注时间。\n   * \n   * @param accessToken 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同\n   * @param openId 用户的唯一标识\n   */\n  public Result<com.belerweb.social.weixin.bean.User> userInfo(String accessToken, String openId) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    weixin.addParameter(params, \"openid\", openId);\n    String json = weixin.get(\"https://api.weixin.qq.com/cgi-bin/user/info\", params);\n    return Result.parse(json, com.belerweb.social.weixin.bean.User.class);\n  }\n\n  /**\n   * 获取所欲关注者列表，包含用户详细信息，该接口采用循环多次获取用户详细信息的方式。如果关注者太多可能会很慢。还会超过微信API调用次数限制。请谨慎调用。建议只在第一次同步关注着信息时调用。\n   */\n  public Result<List<com.belerweb.social.weixin.bean.User>> getFollowUsers() {\n    return getFollowUsers(weixin.getAccessToken().getToken());\n  }\n\n  /**\n   * 获取所欲关注者列表，包含用户详细信息，该接口采用循环多次获取用户详细信息的方式。如果关注者太多可能会很慢。还会超过微信API调用次数限制。请谨慎调用。建议只在第一次同步关注着信息时调用。\n   * \n   * @param accessToken 调用接口凭证\n   */\n  public Result<List<com.belerweb.social.weixin.bean.User>> getFollowUsers(String accessToken) {\n    List<com.belerweb.social.weixin.bean.User> users =\n        new ArrayList<com.belerweb.social.weixin.bean.User>();\n    Result<GetFollowersResult> followersResult = getFollowers(accessToken);\n    if (followersResult.success()) {\n      for (String openId : followersResult.getResult().getOpenIds()) {\n        Result<com.belerweb.social.weixin.bean.User> userResult = userInfo(accessToken, openId);\n        if (userResult.success()) {\n          users.add(userResult.getResult());\n        } else {\n          return new Result<List<com.belerweb.social.weixin.bean.User>>(userResult.getError());\n        }\n      }\n\n      return new Result<List<com.belerweb.social.weixin.bean.User>>(users);\n    }\n    return new Result<List<com.belerweb.social.weixin.bean.User>>(followersResult.getError());\n  }\n\n  /**\n   * 获取所欲关注者列表\n   */\n  public Result<GetFollowersResult> getFollowers() {\n    return getFollowers(weixin.getAccessToken().getToken());\n  }\n\n  /**\n   * 获取所欲关注者列表\n   * \n   * @param accessToken 调用接口凭证\n   */\n  public Result<GetFollowersResult> getFollowers(String accessToken) {\n    GetFollowersResult result = new GetFollowersResult();\n    List<String> openIds = new ArrayList<String>();\n    Result<GetFollowersResult> followers = getFollowers(accessToken, null);\n    while (followers.success()) {\n      for (String openId : followers.getResult().getOpenIds()) {\n        openIds.add(openId);\n      }\n      String nextOpenid = followers.getResult().getNextOpenid();\n      if (StringUtils.isBlank(nextOpenid) || followers.getResult().getTotal() == openIds.size()) {\n        break;\n      }\n      followers = getFollowers(accessToken, nextOpenid);\n    }\n    if (!followers.success()) {\n      return new Result<GetFollowersResult>(followers.getError());\n    }\n    result.setTotal(openIds.size());\n    result.setCount(openIds.size());\n    result.setOpenIds(openIds);\n    return new Result<GetFollowersResult>(result);\n  }\n\n  /**\n   * 获取关注者列表\n   * \n   * 公众号可通过本接口来获取帐号的关注者列表，关注者列表由一串OpenID（加密后的微信号，每个用户对每个公众号的OpenID是唯一的）组成。一次拉取调用最多拉取10000个关注者的OpenID\n   * ，可以通过多次拉取的方式来满足需求。\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=获取关注者列表\n   * \n   * @param accessToken 调用接口凭证\n   * @param openId 第一个拉取的OPENID，不填默认从头开始拉取\n   */\n  public Result<GetFollowersResult> getFollowers(String accessToken, String openId) {\n    List<NameValuePair> params = new ArrayList<NameValuePair>();\n    weixin.addParameter(params, \"access_token\", accessToken);\n    weixin.addNotNullParameter(params, \"next_openid\", openId);\n    String json = weixin.get(\"https://api.weixin.qq.com/cgi-bin/user/get\", params);\n    return Result.parse(json, GetFollowersResult.class);\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/api/Weixin.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.entity.StringEntity;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.SDK;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.exception.SocialException;\nimport com.belerweb.social.weixin.bean.AccessToken;\nimport com.belerweb.social.weixin.bean.ApiTicket;\nimport com.belerweb.social.weixin.bean.JSApiTicket;\nimport com.belerweb.social.weixin.bean.Message;\nimport com.belerweb.social.weixin.bean.QRCreation;\nimport com.belerweb.social.weixin.bean.QRTicket;\nimport com.belerweb.social.weixin.bean.QRType;\n\n/**\n * 微信SDK\n */\npublic final class Weixin extends SDK {\n\n  private static final Charset DEFAULT_CHARSET = Charset.forName(\"UTF-8\");\n\n  private String appId;\n  private String secret;\n  private String redirectUri;\n  private String token;\n\n  private OAuth2 oAuth2;\n  private User user;\n  private Group group;\n  private Media media;\n  private Menu menu;\n\n  private AccessToken accessToken;\n  private Date accessTokenTime;\n\n  private JSApiTicket jsApiTicket;\n  private Date jsApiTicketTime;\n\n  private ApiTicket apiTicket;\n  private Date apiTicketTime;\n\n  /**\n   * 只传入token实例化微信SDK，适合于只开发基于微信基础接口的被动接受消息类应用，如智能应答机器人。不推荐适用。\n   * \n   * @param token 在公众平台网站的高级功能 –\n   *        开发模式页，点击“成为开发者”按钮，填写URL和Token，其中URL是开发者用来接收微信服务器数据的接口URL。Token可由开发者可以任意填写\n   *        ，用作生成签名（该Token会和接口URL中包含的Token进行比对，从而验证安全性）。\n   */\n  public Weixin(String token) {\n    super(DEFAULT_CHARSET);\n    this.token = token;\n  }\n\n  /**\n   * 通过appId和secret实例化微信SDK，适合于只开发基于微信高级接口的OAuth2应用，如客服功能。\n   * \n   * @param appId 公众号的唯一标识\n   * @param secret 公众号的appsecret\n   */\n  public Weixin(String appId, String secret) {\n    super(DEFAULT_CHARSET);\n    this.appId = appId;\n    this.secret = secret;\n  }\n\n\n  /**\n   * 通过token、appId和secret实例化微信SDK，支持微信基础和高级接口。推荐适用\n   * \n   * @param appId 公众号的唯一标识\n   * @param secret 公众号的appsecret\n   * @param token 在公众平台网站的高级功能 –\n   *        开发模式页，点击“成为开发者”按钮，填写URL和Token，其中URL是开发者用来接收微信服务器数据的接口URL。Token可由开发者可以任意填写\n   *        ，用作生成签名（该Token会和接口URL中包含的Token进行比对，从而验证安全性）。\n   */\n  public Weixin(String appId, String secret, String token) {\n    this(appId, secret);\n    this.token = token;\n  }\n\n  public Weixin(String appid, String secret, String redirectUri, String token) {\n    this(appid, secret, token);\n    this.redirectUri = redirectUri;\n  }\n\n  /**\n   * 验证消息真实性\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性\n   * \n   * @param signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。\n   * @param timestamp 时间戳\n   * @param nonce 随机数\n   * @return 消息有效返回true，否则返回false\n   */\n  public boolean validate(String signature, String timestamp, String nonce) {\n    String[] chars = new String[] {token, timestamp, nonce,};\n    Arrays.sort(chars);\n    String sha1 = DigestUtils.shaHex(StringUtils.join(chars));\n    if (sha1.equals(signature)) {\n      return true;\n    }\n\n    return false;\n  }\n\n  /**\n   * 签名传入的参数,按照字典顺序排序后连接起来sha1 文档地址：<a href=\n   * \"http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.954-.E5.8D.A1.E5.88.B8.E6.89.A9.E5.B1.95.E5.AD.97.E6.AE.B5.E5.8F.8A.E7.AD.BE.E5.90.8D.E7.94.9F.E6.88.90.E7.AE.97.E6.B3.95\"\n   * >卡券扩展字段及签名生成算法</a>\n   * \n   * @param args\n   * @return\n   */\n  public String signature(String... args) {\n    Arrays.sort(args);\n    return DigestUtils.shaHex(StringUtils.join(args));\n  }\n\n  /**\n   * jsapi_ticket签名算法\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-\n   * JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95\n   * \n   * @param url 网页的url地址不包括 {@code #} 后面的参数(不包含锚的值).但是包括所有的请求参数\n   * @param timestamp 时间戳\n   * @param nonce 随机数\n   * @return 返回签名后的字符串\n   */\n  public String jsapiSignature(String url, long timestamp, String nonce) {\n    StringBuilder content = new StringBuilder();\n    content.append(\"jsapi_ticket=\").append(getJsApiTicket().getTicket());\n    content.append(\"&noncestr=\").append(nonce);\n    content.append(\"&timestamp=\").append(timestamp);\n    content.append(\"&url=\").append(url);\n    return DigestUtils.shaHex(content.toString());\n  }\n\n\n  public String getAppId() {\n    return appId;\n  }\n\n  public void setAppId(String appId) {\n    this.appId = appId;\n  }\n\n  public String getSecret() {\n    return secret;\n  }\n\n  public void setSecret(String secret) {\n    this.secret = secret;\n  }\n\n  public String getRedirectUri() {\n    return redirectUri;\n  }\n\n  public void setRedirectUri(String redirectUri) {\n    this.redirectUri = redirectUri;\n  }\n\n  /**\n   * 获取access token\n   * \n   * access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒，\n   * 重复获取将导致上次获取的access_token失效。\n   * \n   * 公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在开发模式中获得（需要已经成为开发者，且帐号没有异常状态）。\n   */\n  public synchronized AccessToken getAccessToken() {\n    if (accessToken == null || accessTokenTime == null\n        || (new Date().getTime() - accessTokenTime.getTime()) / 1000 > accessToken.getExpiresIn()) {\n      List<NameValuePair> params = new ArrayList<NameValuePair>();\n      addParameter(params, \"appid\", appId);\n      addParameter(params, \"secret\", secret);\n      addParameter(params, \"grant_type\", \"client_credential\");\n      String json =\n          get(\"https://api.weixin.qq.com/cgi-bin/token?\" + StringUtils.join(params, \"&\"), params);\n      Result<AccessToken> result = Result.parse(json, AccessToken.class);\n      if (result.success()) {\n        accessToken = result.getResult();\n        accessTokenTime = new Date();\n      }\n    }\n\n    return accessToken;\n  }\n\n  /**\n   * api_ticket 是用于调用微信卡券JS API的临时票据，有效期为7200 秒，通过access_token 来获取。\n   * \n   * 文档地址:<a href=\n   * \"http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E8.8E.B7.E5.8F.96api_ticket\"\n   * >获取api_ticket</a>\n   * \n   * 由于获取api_ticket 的api 调用次数非常有限，频繁刷新api_ticket 会导致api调用受限，影响自身业务，开发者需在自己的服务存储与更新api_ticket。\n   */\n  public synchronized ApiTicket getApiTicket() {\n    if (apiTicket == null || apiTicketTime == null\n        || (new Date().getTime() - apiTicketTime.getTime()) / 1000 > apiTicket.getExpiresIn()) {\n      List<NameValuePair> params = new ArrayList<NameValuePair>();\n      addParameter(params, \"access_token\", getAccessToken().getToken());\n      addParameter(params, \"type\", \"wx_card\");\n      String json =\n          get(\"https://api.weixin.qq.com/cgi-bin/ticket/getticket?\" + StringUtils.join(params, \"&\"),\n              params);\n      Result<ApiTicket> result = Result.parse(json, ApiTicket.class);\n      if (result.success()) {\n        apiTicket = result.getResult();\n        apiTicketTime = new Date();\n      }\n    }\n\n    return apiTicket;\n  }\n\n  /**\n   * jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下，jsapi_ticket的有效期为7200秒，通过access_token来获取。\n   * \n   * 文档地址:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-\n   * JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95\n   * \n   * 由于获取jsapi_ticket的api调用次数非常有限，频繁刷新jsapi_ticket会导致api调用受限，影响自身业务，开发者必须在自己的服务全局缓存jsapi_ticket 。\n   */\n  public synchronized JSApiTicket getJsApiTicket() {\n    if (jsApiTicket == null || jsApiTicketTime == null\n        || (new Date().getTime() - jsApiTicketTime.getTime()) / 1000 > jsApiTicket.getExpiresIn()) {\n      List<NameValuePair> params = new ArrayList<NameValuePair>();\n      addParameter(params, \"access_token\", getAccessToken().getToken());\n      addParameter(params, \"type\", \"jsapi\");\n      String json =\n          get(\"https://api.weixin.qq.com/cgi-bin/ticket/getticket?\" + StringUtils.join(params, \"&\"),\n              params);\n      Result<JSApiTicket> result = Result.parse(json, JSApiTicket.class);\n      if (result.success()) {\n        jsApiTicket = result.getResult();\n        jsApiTicketTime = new Date();\n      }\n    }\n\n    return jsApiTicket;\n  }\n\n  /**\n   * 自动获取accessToken并创建二维码ticket\n   * \n   * 每次创建二维码ticket需要提供一个开发者自行设定的参数（scene_id）\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码\n   * \n   * @param type 二维码类型，QR_SCENE为临时,QR_LIMIT_SCENE为永久\n   * @param sceneId 场景值ID，临时二维码时为32位整型，永久二维码时最大值为1000\n   */\n  public Result<QRTicket> createQR(QRType type, Integer sceneId) {\n    return createQR(getAccessToken().getToken(), type, sceneId);\n  }\n\n  /**\n   * 创建二维码ticket\n   * \n   * 每次创建二维码ticket需要提供一个开发者自行设定的参数（scene_id）\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=生成带参数的二维码\n   * \n   * @param accessToken access_token是公众号的全局唯一票据\n   * @param type 二维码类型，QR_SCENE为临时,QR_LIMIT_SCENE为永久\n   * @param sceneId 场景值ID，临时二维码时为32位整型，永久二维码时最大值为1000\n   */\n  public Result<QRTicket> createQR(String accessToken, QRType type, Integer sceneId) {\n    QRCreation request = new QRCreation();\n    request.setType(type);\n    request.setSceneId(sceneId);\n    try {\n      String json =\n          post(\"https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=\" + accessToken,\n              new StringEntity(request.toString()));\n      return Result.parse(json, QRTicket.class);\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 发送客服消息\n   * \n   * 当用户主动发消息给公众号的时候，微信将会把消息数据推送给开发者，开发者在一段时间内（目前为24小时）可以调用客服消息接口，通过POST一个JSON数据包来发送消息给普通用户，\n   * 在24小时内不限制发送次数。此接口主要用于客服等有人工消息处理环节的功能，方便开发者为用户提供更加优质的服务。\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息\n   * \n   * @param message 消息\n   */\n  public Result<Boolean> sendCustomMessage(Message message) {\n    return sendCustomMessage(getAccessToken().getToken(), message);\n  }\n\n  /**\n   * 发送客服消息\n   * \n   * 当用户主动发消息给公众号的时候，微信将会把消息数据推送给开发者，开发者在一段时间内（目前为24小时）可以调用客服消息接口，通过POST一个JSON数据包来发送消息给普通用户，\n   * 在24小时内不限制发送次数。此接口主要用于客服等有人工消息处理环节的功能，方便开发者为用户提供更加优质的服务。\n   * \n   * 文档地址：http://mp.weixin.qq.com/wiki/index.php?title=发送客服消息\n   * \n   * @param accessToken access_token是公众号的全局唯一票据\n   * @param message 消息\n   */\n  public Result<Boolean> sendCustomMessage(String accessToken, Message message) {\n    try {\n      // {\"errcode\":0,\"errmsg\":\"ok\"}\n      // {\"errcode\":45015,\"errmsg\":\"response out of time limit\"}\n      String json =\n          post(\"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=\" + accessToken,\n              new StringEntity(message.toJSON(), \"UTF-8\"));\n      return new Result<Boolean>(Error.parse(new JSONObject(json)));\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 发送模板消息\n   * \n   * <p>\n   * 为了保证用户不受到骚扰，在开发者出现需要主动提醒、通知用户时，才允许开发者在公众平台网站中模板消息库中选择模板，选择后获得模板ID，再根据模板ID向用户主动推送提醒、通知消息。\n   * </p>\n   * <p>\n   * 模板消息调用时主要需要模板ID和模板中各参数的赋值内容。请注意：\n   * <ol>\n   * <li>模板中参数内容必须以\".DATA\"结尾，否则视为保留字;</li>\n   * <li>模板保留符号\"{{ }}\"</li>\n   * </ol>\n   * </p>\n   * 具体调用方法\n   * <p>\n   * 第一步：获取模板ID<br />\n   * 通过在模板消息功能的模板库中使用需要的模板，可以获得模板ID。\n   * </p>\n   * <p>\n   * 第二步：请求接口\n   * </p>\n   * \n   * 文档地址：https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&lang=zh_CN\n   * \n   * @param message 消息\n   */\n  public Result<Boolean> sendTemplateMessage(Message message) {\n    return sendTemplateMessage(getAccessToken().getToken(), message);\n  }\n\n  /**\n   * 发送模板消息\n   * \n   * <p>\n   * 为了保证用户不受到骚扰，在开发者出现需要主动提醒、通知用户时，才允许开发者在公众平台网站中模板消息库中选择模板，选择后获得模板ID，再根据模板ID向用户主动推送提醒、通知消息。\n   * </p>\n   * <p>\n   * 模板消息调用时主要需要模板ID和模板中各参数的赋值内容。请注意：\n   * <ol>\n   * <li>模板中参数内容必须以\".DATA\"结尾，否则视为保留字;</li>\n   * <li>模板保留符号\"{{ }}\"</li>\n   * </ol>\n   * </p>\n   * 具体调用方法\n   * <p>\n   * 第一步：获取模板ID<br />\n   * 通过在模板消息功能的模板库中使用需要的模板，可以获得模板ID。\n   * </p>\n   * <p>\n   * 第二步：请求接口\n   * </p>\n   * \n   * 文档地址：https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&lang=zh_CN\n   * \n   * @param accessToken access_token是公众号的全局唯一票据\n   * @param message 消息\n   */\n  public Result<Boolean> sendTemplateMessage(String accessToken, Message message) {\n    try {\n      String json =\n          post(\"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=\"\n              + accessToken, new StringEntity(message.toJSON(), \"UTF-8\"));\n      return new Result<Boolean>(Error.parse(new JSONObject(json)));\n    } catch (UnsupportedEncodingException e) {\n      throw new SocialException(e);\n    }\n  }\n\n  /**\n   * 在公众平台网站的高级功能 –\n   * 开发模式页，点击“成为开发者”按钮，填写URL和Token，其中URL是开发者用来接收微信服务器数据的接口URL。Token可由开发者可以任意填写，用作生成签名（\n   * 该Token会和接口URL中包含的Token进行比对，从而验证安全性）\n   */\n  public String getToken() {\n    return token;\n  }\n\n  public void setToken(String token) {\n    this.token = token;\n  }\n\n  public OAuth2 getOAuth2() {\n    if (oAuth2 == null) {\n      oAuth2 = new OAuth2(this);\n    }\n\n    return oAuth2;\n  }\n\n  public User getUser() {\n    if (user == null) {\n      user = new User(this);\n    }\n\n    return user;\n  }\n\n  public Group getGroup() {\n    if (group == null) {\n      group = new Group(this);\n    }\n\n    return group;\n  }\n\n  public Media getMedia() {\n    if (media == null) {\n      media = new Media(this);\n    }\n\n    return media;\n  }\n\n  public Menu getMenu() {\n    if (menu == null) {\n      menu = new Menu(this);\n    }\n\n    return menu;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/AccessToken.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 网页授权接口调用凭证\n */\npublic class AccessToken extends JsonBean {\n\n  public AccessToken() {}\n\n  private AccessToken(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String token;// 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同\n  private Long expiresIn;// access_token接口调用凭证超时时间，单位（秒）\n  private String refreshToken;// 用户刷新access_token\n  private String openId;// 用户唯一标识，请注意，在未关注公众号时，用户访问公众号的网页，也会产生一个用户和公众号唯一的OpenID\n  private Scope scope;// 用户授权的作用域，使用逗号（,）分隔\n  private String unionid;// 当且仅当该网站应用已获得该用户的userinfo授权时，才会出现该字段\n\n  /**\n   * 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同\n   */\n  public String getToken() {\n    return token;\n  }\n\n  public void setToken(String token) {\n    this.token = token;\n  }\n\n  /**\n   * access_token接口调用凭证超时时间，单位（秒）\n   */\n  public Long getExpiresIn() {\n    return expiresIn;\n  }\n\n  public void setExpiresIn(Long expiresIn) {\n    this.expiresIn = expiresIn;\n  }\n\n  /**\n   * 用户刷新access_token\n   */\n  public String getRefreshToken() {\n    return refreshToken;\n  }\n\n  public void setRefreshToken(String refreshToken) {\n    this.refreshToken = refreshToken;\n  }\n\n  /**\n   * 用户唯一标识，请注意，在未关注公众号时，用户访问公众号的网页，也会产生一个用户和公众号唯一的OpenID\n   */\n  public String getOpenId() {\n    return openId;\n  }\n\n  public void setOpenId(String openId) {\n    this.openId = openId;\n  }\n\n  /**\n   * 用户授权的作用域，使用逗号（,）分隔\n   */\n  public Scope getScope() {\n    return scope;\n  }\n\n  public void setScope(Scope scope) {\n    this.scope = scope;\n  }\n\n  public String getUnionid() {\n    return unionid;\n  }\n\n  public void setUnionid(String unionid) {\n    this.unionid = unionid;\n  }\n\n  public static AccessToken parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    AccessToken obj = new AccessToken(jsonObject);\n    obj.token = jsonObject.getString(\"access_token\");\n    obj.openId = Result.toString(jsonObject.opt(\"openid\"));\n    obj.expiresIn = Result.parseLong(jsonObject.opt(\"expires_in\"));\n    obj.refreshToken = Result.toString(jsonObject.opt(\"refresh_token\"));\n    obj.scope = Scope.parse(jsonObject.opt(\"scope\"));\n    obj.unionid = Result.toString(jsonObject.opt(\"unionid\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/ApiTicket.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * api_ticket 是用于调用微信卡券JS API的临时票据，有效期为7200 秒，通过access_token 来获取。\n */\npublic class ApiTicket extends JsonBean {\n\n  public ApiTicket() {}\n\n  private ApiTicket(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String ticket;// ticket是公众号用于调用微信卡券JS API的临时票据\n  private Long expiresIn;// api_ticket接口调用凭证超时时间，单位（秒）\n\n  public String getTicket() {\n    return ticket;\n  }\n\n  public void setTicket(String ticket) {\n    this.ticket = ticket;\n  }\n\n  public Long getExpiresIn() {\n    return expiresIn;\n  }\n\n  public void setExpiresIn(Long expiresIn) {\n    this.expiresIn = expiresIn;\n  }\n\n  public static ApiTicket parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    ApiTicket obj = new ApiTicket(jsonObject);\n    obj.ticket = jsonObject.getString(\"ticket\");\n    obj.expiresIn = Result.parseLong(jsonObject.opt(\"expires_in\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Article.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n/**\n * 图文\n */\npublic class Article {\n\n  private String title;// 图文消息标题\n  private String description;// 图文消息描述\n  private String picUrl;// 图片链接，支持JPG、PNG格式，较好的效果为大图360*200，小图200*200\n  private String url;// 点击图文消息跳转链接\n\n  /**\n   * 图文消息标题\n   */\n  public String getTitle() {\n    return title;\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n  }\n\n  /**\n   * 图文消息描述\n   */\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  /**\n   * 图片链接，支持JPG、PNG格式，较好的效果为大图360*200，小图200*200\n   */\n  public String getPicUrl() {\n    return picUrl;\n  }\n\n  public void setPicUrl(String picUrl) {\n    this.picUrl = picUrl;\n  }\n\n  /**\n   * 点击图文消息跳转链接\n   */\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/EventType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 事件类型\n */\npublic enum EventType {\n\n  /**\n   * 订阅\n   */\n  SUBSCRIBE(\"subscribe\"),\n\n  /**\n   * 取消订阅\n   */\n  UNSUBSCRIBE(\"unsubscribe\"),\n\n  /**\n   * 扫描二维码：用户已关注时的事件推送\n   */\n  SCAN(\"SCAN\"),\n\n  /**\n   * 上报地理位置事件\n   */\n  LOCATION(\"LOCATION\"),\n\n  /**\n   * 点击链接事件\n   */\n  VIEW(\"VIEW\"),\n\n  /**\n   * 自定义菜单事件\n   */\n  CLICK(\"CLICK\");\n\n  private String type;\n\n  private EventType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public static EventType parse(Object val) {\n    if (SUBSCRIBE.type.equals(val)) {\n      return SUBSCRIBE;\n    }\n    if (UNSUBSCRIBE.type.equals(val)) {\n      return UNSUBSCRIBE;\n    }\n    if (SCAN.type.equals(val)) {\n      return SCAN;\n    }\n    if (LOCATION.type.equals(val)) {\n      return LOCATION;\n    }\n    if (CLICK.type.equals(val)) {\n      return CLICK;\n    }\n    if (VIEW.type.equals(val)) {\n      return VIEW;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/GetFollowersResult.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 获取关注者列表结果\n */\npublic class GetFollowersResult extends JsonBean {\n\n  public GetFollowersResult() {}\n\n  private GetFollowersResult(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Integer total;// 关注该公众账号的总用户数\n  private Integer count;// 拉取的OPENID个数，最大值为10000\n  private List<String> openIds;// OPENID的列表\n  private String nextOpenid;// 拉取列表的后一个用户的OPENID\n\n  /**\n   * 关注该公众账号的总用户数\n   */\n  public Integer getTotal() {\n    return total;\n  }\n\n  public void setTotal(Integer total) {\n    this.total = total;\n  }\n\n  /**\n   * 拉取的OPENID个数，最大值为10000\n   */\n  public Integer getCount() {\n    return count;\n  }\n\n  public void setCount(Integer count) {\n    this.count = count;\n  }\n\n  /**\n   * OPENID的列表\n   */\n  public List<String> getOpenIds() {\n    return openIds;\n  }\n\n  public void setOpenIds(List<String> openIds) {\n    this.openIds = openIds;\n  }\n\n  /**\n   * 拉取列表的后一个用户的OPENID\n   */\n  public String getNextOpenid() {\n    return nextOpenid;\n  }\n\n  public void setNextOpenid(String nextOpenid) {\n    this.nextOpenid = nextOpenid;\n  }\n\n  public static GetFollowersResult parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    GetFollowersResult obj = new GetFollowersResult(jsonObject);\n    obj.total = Result.parseInteger(jsonObject.get(\"total\"));\n    obj.count = Result.parseInteger(jsonObject.get(\"count\"));\n    obj.nextOpenid = Result.toString(jsonObject.opt(\"next_openid\"));\n    JSONArray openIdArray = jsonObject.getJSONObject(\"data\").getJSONArray(\"openid\");\n    List<String> openIds = new ArrayList<String>();\n    for (int i = 0; i < openIdArray.length(); i++) {\n      openIds.add(openIdArray.getString(i));\n    }\n    obj.openIds = openIds;\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Group.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 分组\n */\npublic class Group extends JsonBean {\n\n  public Group() {}\n\n  private Group(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String id;// 分组id，由微信分配\n  private String name;// 分组名字，UTF8编码\n  private Integer count;// 分组内用户数量\n\n  /**\n   * 分组id，由微信分配\n   */\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  /**\n   * 分组名字，UTF8编码\n   */\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  /**\n   * 分组内用户数量\n   */\n  public Integer getCount() {\n    return count;\n  }\n\n  public void setCount(Integer count) {\n    this.count = count;\n  }\n\n  public static Group parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Group obj = new Group(jsonObject);\n    obj.id = Result.toString(jsonObject.get(\"id\"));\n    obj.name = Result.toString(jsonObject.get(\"name\"));\n    obj.count = Result.parseInteger(jsonObject.opt(\"count\"));\n    return obj;\n  }\n\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/JSApiTicket.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下，jsapi_ticket的有效期为7200秒，通过access_token来获取。\n */\npublic class JSApiTicket extends JsonBean {\n\n  public JSApiTicket() {}\n\n  private JSApiTicket(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String ticket;// ticket是公众号用于调用微信JS接口的临时票据\n  private Long expiresIn;// jsapi_ticket接口调用凭证超时时间，单位（秒）\n\n  public String getTicket() {\n    return ticket;\n  }\n\n  public void setTicket(String ticket) {\n    this.ticket = ticket;\n  }\n\n  public Long getExpiresIn() {\n    return expiresIn;\n  }\n\n  public void setExpiresIn(Long expiresIn) {\n    this.expiresIn = expiresIn;\n  }\n\n  public static JSApiTicket parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    JSApiTicket obj = new JSApiTicket(jsonObject);\n    obj.ticket = jsonObject.getString(\"ticket\");\n    obj.expiresIn = Result.parseLong(jsonObject.opt(\"expires_in\"));\n    return obj;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Media.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 媒体文件\n */\npublic class Media {\n\n  private String id;\n  private String name;\n  private String contentType;\n  private byte[] content;\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getContentType() {\n    return contentType;\n  }\n\n  public void setContentType(String contentType) {\n    this.contentType = contentType;\n  }\n\n  public byte[] getContent() {\n    return content;\n  }\n\n  public void setContent(byte[] content) {\n    this.content = content;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/MediaType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 媒体文件类型\n */\npublic enum MediaType {\n\n  /**\n   * 图片\n   */\n  IMAGE(\"image\"),\n\n  /**\n   * 语音\n   */\n  VOICE(\"voice\"),\n\n  /**\n   * 语音\n   */\n  VOICE_AMR(\"voice\"),\n\n  /**\n   * 语音\n   */\n  VOICE_MP3(\"voice\"),\n\n  /**\n   * 视频\n   */\n  VIDEO(\"video\"),\n\n  /**\n   * 缩略图\n   */\n  THUMB(\"thumb\");\n\n  private String type;\n\n  private MediaType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public String contentType() {\n    if (this == IMAGE || this == THUMB) {\n      return \"image/jpeg\";\n    }\n    if (this == VOICE || this == VOICE_AMR) {\n      return \"audio/amr\";\n    }\n    if (this == VOICE_MP3) {\n      return \"audio/mp3\";\n    }\n    if (this == VIDEO) {\n      return \"audio/mp4\";\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Menu.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport java.util.List;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n\n/**\n * 自定义菜单\n */\npublic class Menu extends JsonBean {\n\n  public Menu() {}\n\n  private Menu(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private MenuType type;\n  private String key;\n  private String url;\n  private String name;\n  private List<Menu> subs;\n\n  public MenuType getType() {\n    return type;\n  }\n\n  public void setType(MenuType type) {\n    this.type = type;\n  }\n\n  public String getKey() {\n    return key;\n  }\n\n  public void setKey(String key) {\n    this.key = key;\n  }\n\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public List<Menu> getSubs() {\n    return subs;\n  }\n\n  public void setSubs(List<Menu> subs) {\n    this.subs = subs;\n  }\n\n  public static Menu parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Menu obj = new Menu(jsonObject);\n    obj.name = Result.toString(jsonObject.get(\"name\"));\n    obj.key = Result.toString(jsonObject.opt(\"key\"));\n    obj.url = Result.toString(jsonObject.opt(\"url\"));\n    obj.type = MenuType.parse(jsonObject.opt(\"type\"));\n    obj.subs = Result.parse(jsonObject.optJSONArray(\"sub_button\"), Menu.class);\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/MenuType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 自定义菜单类型\n */\npublic enum MenuType {\n\n  /**\n   * 用户点击click类型按钮后，微信服务器会通过消息接口推送消息类型为event的结构给开发者（参考消息接口指南），并且带上按钮中开发者填写的key值，\n   * 开发者可以通过自定义的key值与用户进行交互；\n   */\n  CLICK(\"click\"),\n\n  /**\n   * 用户点击view类型按钮后，微信客户端将会打开开发者在按钮中填写的url值 （即网页链接），达到打开网页的目的，建议与网页授权获取用户基本信息接口结合，获得用户的登入个人信息。\n   */\n  VIEW(\"view\"),\n\n  /**\n   * 扫码推事件,用户点击按钮后，微信客户端将调起扫一扫工具，完成扫码操作后显示扫描结果（如果是URL，将进入URL），且会将扫码的结果传给开发者，开发者可以下发消息。\n   */\n  SCANCODE_PUSH(\"scancode_push\"),\n\n  /**\n   * 扫码推事件且弹出“消息接收中”提示框,用户点击按钮后，微信客户端将调起扫一扫工具，完成扫码操作后，将扫码的结果传给开发者，同时收起扫一扫工具，\n   * 然后弹出“消息接收中”提示框，随后可能会收到开发者下发的消息。\n   */\n  SCANCODE_WAITMSG(\"scancode_waitmsg\"),\n\n  /**\n   * 弹出系统拍照发图,用户点击按钮后，微信客户端将调起系统相机，完成拍照操作后，会将拍摄的相片发送给开发者，并推送事件给开发者，同时收起系统相机，随后可能会收到开发者下发的消息。\n   */\n  PIC_SYSPHOTO(\"pic_sysphoto\"),\n\n  /**\n   * 弹出拍照或者相册发图,用户点击按钮后，微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。\n   */\n  PIC_PHOTO_OR_ALBUM(\"pic_photo_or_album\"),\n\n  /**\n   * 弹出微信相册发图器,用户点击按钮后，微信客户端将调起微信相册，完成选择操作后，将选择的相片发送给开发者的服务器， 并推送事件给开发者，同时收起相册，随后可能会收到开发者下发的消息。\n   */\n  PIC_WEIXIN(\"pic_weixin\"),\n\n  /**\n   * 弹出地理位置选择器,用户点击按钮后，微信客户端将调起地理位置选择工具，完成选择操作后，将选择的地理位置发送给开发者的服务器， 同时收起位置选择工具，随后可能会收到开发者下发的消息。\n   */\n  LOCATION_SELECT(\"location_select\"), ;\n\n  private String type;\n\n  private MenuType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public static MenuType parse(Object val) {\n    if (CLICK.type.equals(val)) {\n      return CLICK;\n    }\n    if (VIEW.type.equals(val)) {\n      return VIEW;\n    }\n    return null;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Message.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.json.XML;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 普通消息\n */\npublic class Message extends JsonBean {\n\n  public Message() {}\n\n  public Message(MsgType msgType) {\n    this.msgType = msgType;\n  }\n\n  private Message(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private Long msgId;// 消息id\n  private String fromUser;// 发送方帐号（一个OpenID）\n  private String toUser;// 开发者微信号\n  private Date createTime;// 消息创建时间\n  private MsgType msgType;// 消息类型\n  private String content;// 文本消息内容\n  private String mediaId;// 消息媒体id，可以调用多媒体文件下载接口拉取数据。\n  private String picUrl;// 图片链接\n  private VoiceType voiceType;// 语音格式\n  private String recognition;// 语音识别结果\n  private String thumbMediaId;// 视频消息缩略图的媒体id，可以调用多媒体文件下载接口拉取数据。\n  private Double lon;// 地理位置经度\n  private Double lat;// 地理位置纬度\n  private Integer scale;// 地图缩放大小\n  private String label;// 地理位置信息\n  private String title;// 消息标题\n  private String description;// 消息描述\n  private String url;// 消息链接\n  private String musicUrl;// 音乐链接\n  private String hqMusicUrl;// 高质量音乐链接，WIFI环境优先使用该链接播放音乐\n  private EventType eventType;// 事件类型\n  private String eventKey;// 事件KEY值\n  private String ticket;// 二维码的ticket，可用来换取二维码图片\n  private Double precision;// 地理位置精度\n  private List<Article> articles;// 多条图文消息信息，默认第一个item为大图\n\n  /**\n   * 模板消息特有的属性\n   */\n  private String templateId;// 模板消息的id\n  private String topColor;// 模板消息头部的颜色 eg:#FF0000\n  private List<Variable> variables;// 模板消息的变量\n\n\n  /**\n   * 消息id\n   */\n  public Long getMsgId() {\n    return msgId;\n  }\n\n  public void setMsgId(Long msgId) {\n    this.msgId = msgId;\n  }\n\n  /**\n   * 发送方帐号\n   */\n  public String getFromUser() {\n    return fromUser;\n  }\n\n  public void setFromUser(String fromUser) {\n    this.fromUser = fromUser;\n  }\n\n  /**\n   * 接受方帐号\n   */\n  public String getToUser() {\n    return toUser;\n  }\n\n  public void setToUser(String toUser) {\n    this.toUser = toUser;\n  }\n\n  /**\n   * 消息创建时间\n   */\n  public Date getCreateTime() {\n    return createTime;\n  }\n\n  public void setCreateTime(Date createTime) {\n    this.createTime = createTime;\n  }\n\n  /**\n   * 消息类型\n   */\n  public MsgType getMsgType() {\n    return msgType;\n  }\n\n  public void setMsgType(MsgType msgType) {\n    this.msgType = msgType;\n  }\n\n  /**\n   * 文本消息内容\n   */\n  public String getContent() {\n    return content;\n  }\n\n  public void setContent(String content) {\n    this.content = content;\n  }\n\n  /**\n   * 图片／语音／视频特有：消息媒体id，可以调用多媒体文件下载接口拉取数据。\n   */\n  public String getMediaId() {\n    return mediaId;\n  }\n\n  public void setMediaId(String mediaId) {\n    this.mediaId = mediaId;\n  }\n\n  /**\n   * 图片消息特有：图片链接\n   */\n  public String getPicUrl() {\n    return picUrl;\n  }\n\n  public void setPicUrl(String picUrl) {\n    this.picUrl = picUrl;\n  }\n\n  /**\n   * 语音消息特有：语音格式\n   */\n  public VoiceType getVoiceType() {\n    return voiceType;\n  }\n\n  public void setVoiceType(VoiceType voiceType) {\n    this.voiceType = voiceType;\n  }\n\n\n  /**\n   * 语音消息特有： 语音识别结果，UTF8编码，只有在开启语音识别后才有此结果\n   */\n  public String getRecognition() {\n    return recognition;\n  }\n\n  public void setRecognition(String recognition) {\n    this.recognition = recognition;\n  }\n\n  /**\n   * 视频消息/音乐消息特有：视频消息缩略图的媒体id，可以调用多媒体文件下载接口拉取数据。\n   */\n  public String getThumbMediaId() {\n    return thumbMediaId;\n  }\n\n  public void setThumbMediaId(String thumbMediaId) {\n    this.thumbMediaId = thumbMediaId;\n  }\n\n  /**\n   * 位置消息特有：地理位置经度\n   */\n  public Double getLon() {\n    return lon;\n  }\n\n  public void setLon(Double lon) {\n    this.lon = lon;\n  }\n\n  /**\n   * 位置消息特有：地理位置纬度\n   */\n  public Double getLat() {\n    return lat;\n  }\n\n  public void setLat(Double lat) {\n    this.lat = lat;\n  }\n\n  /**\n   * 位置消息特有： 地图缩放大小\n   */\n  public Integer getScale() {\n    return scale;\n  }\n\n  public void setScale(Integer scale) {\n    this.scale = scale;\n  }\n\n  /**\n   * 位置消息特有： 地理位置信息\n   */\n  public String getLabel() {\n    return label;\n  }\n\n  public void setLabel(String label) {\n    this.label = label;\n  }\n\n  /**\n   * 链接消息/音乐消息特有：消息标题\n   */\n  public String getTitle() {\n    return title;\n  }\n\n  public void setTitle(String title) {\n    this.title = title;\n  }\n\n  /**\n   * 链接消息/音乐消息特有：消息描述\n   */\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  /**\n   * 链接消息特有：消息链接\n   */\n  public String getUrl() {\n    return url;\n  }\n\n  public void setUrl(String url) {\n    this.url = url;\n  }\n\n  /**\n   * 音乐链接\n   */\n  public String getMusicUrl() {\n    return musicUrl;\n  }\n\n  public void setMusicUrl(String musicUrl) {\n    this.musicUrl = musicUrl;\n  }\n\n  /**\n   * 高质量音乐链接，WIFI环境优先使用该链接播放音乐\n   */\n  public String getHqMusicUrl() {\n    return hqMusicUrl;\n  }\n\n  public void setHqMusicUrl(String hqMusicUrl) {\n    this.hqMusicUrl = hqMusicUrl;\n  }\n\n  /**\n   * 事件特有：事件类型\n   */\n  public EventType getEventType() {\n    return eventType;\n  }\n\n  public void setEventType(EventType eventType) {\n    this.eventType = eventType;\n  }\n\n  /**\n   * 事件KEY值，QR扫描／自定义菜单时特有\n   */\n  public String getEventKey() {\n    return eventKey;\n  }\n\n  public void setEventKey(String eventKey) {\n    this.eventKey = eventKey;\n  }\n\n  /**\n   * 扫描二维码，用户已关注时的事件推送特有\n   * <p/>\n   * 二维码的ticket，可用来换取二维码图片\n   */\n  public String getTicket() {\n    return ticket;\n  }\n\n  public void setTicket(String ticket) {\n    this.ticket = ticket;\n  }\n\n  /**\n   * 上报地理位置事件特有： 地理位置精度\n   */\n  public Double getPrecision() {\n    return precision;\n  }\n\n  public void setPrecision(Double precision) {\n    this.precision = precision;\n  }\n\n  /**\n   * 多条图文消息信息，默认第一个item为大图\n   */\n  public List<Article> getArticles() {\n    return articles;\n  }\n\n  public void setArticles(List<Article> articles) {\n    this.articles = articles;\n  }\n\n  /**\n   * 模板消息id\n   */\n  public String getTemplateId() {\n    return templateId;\n  }\n\n  public void setTemplateId(String templateId) {\n    this.templateId = templateId;\n  }\n\n  /**\n   * 模板消息头部颜色\n   */\n  public String getTopColor() {\n    return topColor;\n  }\n\n  public void setTopColor(String topColor) {\n    this.topColor = topColor;\n  }\n\n  /**\n   * 模板消息变量\n   * \n   * @return\n   */\n  public List<Variable> getVariables() {\n    if (variables == null) {\n      variables = new ArrayList<Variable>();\n    }\n    return variables;\n  }\n\n  public void setVariables(List<Variable> variables) {\n    this.variables = variables;\n  }\n\n  public Message addVariable(Variable variable) {\n    getVariables().add(variable);\n    return this;\n  }\n\n  /**\n   * 将Message转换成XML格式，用于发送被动响应消息\n   */\n  public String toXML() {\n    JSONObject obj = new JSONObject();\n    obj.put(\"MsgType\", msgType.toString());\n    obj.put(\"ToUserName\", toUser);\n    obj.put(\"FromUserName\", fromUser);\n    obj.put(\"CreateTime\", (Long) (createTime.getTime() / 1000));\n    if (msgType == MsgType.TEXT) {\n      obj.put(\"Content\", content);\n    }\n    if (msgType == MsgType.IMAGE) {\n      JSONObject image = new JSONObject();\n      image.put(\"MediaId\", mediaId);\n      obj.put(\"Image\", image);\n    }\n    if (msgType == MsgType.VOICE) {\n      JSONObject voice = new JSONObject();\n      voice.put(\"MediaId\", mediaId);\n      obj.put(\"Voice\", voice);\n    }\n    if (msgType == MsgType.VIDEO) {\n      JSONObject video = new JSONObject();\n      video.put(\"MediaId\", mediaId);\n      video.put(\"ThumbMediaId\", thumbMediaId);\n      obj.put(\"Video\", video);\n    }\n    if (msgType == MsgType.MUSIC) {\n      JSONObject music = new JSONObject();\n      music.put(\"Title\", title);\n      music.put(\"Description\", description);\n      music.put(\"MusicURL\", musicUrl);\n      music.put(\"HQMusicUrl\", hqMusicUrl);\n      music.put(\"ThumbMediaId\", thumbMediaId);\n      obj.put(\"Music\", music);\n    }\n    if (msgType == MsgType.NEWS) {\n      obj.put(\"ArticleCount\", articles.size());\n      JSONArray array = new JSONArray();\n      for (Article article : articles) {\n        JSONObject itemWrapper = new JSONObject();\n        JSONObject item = new JSONObject();\n        item.put(\"Title\", article.getTitle());\n        item.put(\"Description\", article.getDescription());\n        item.put(\"PicUrl\", article.getPicUrl());\n        item.put(\"Url\", article.getUrl());\n        itemWrapper.put(\"item\", item);\n        array.put(itemWrapper);\n      }\n      obj.put(\"Articles\", array);\n    }\n    return XML.toString(obj, \"xml\").replaceAll(\"</Articles><Articles>\", \"\");\n  }\n\n  /**\n   * 将Message转换成JSON格式，用于发送客服消息\n   */\n  public String toJSON() {\n    JSONObject obj = new JSONObject();\n    obj.put(\"msgtype\", msgType.toString());\n    obj.put(\"touser\", toUser);\n    if (msgType == MsgType.TEXT) {\n      JSONObject text = new JSONObject();\n      text.put(\"content\", content);\n      obj.put(\"text\", text);\n    }\n    if (msgType == MsgType.IMAGE) {\n      JSONObject image = new JSONObject();\n      image.put(\"media_id\", mediaId);\n      obj.put(\"image\", image);\n    }\n    if (msgType == MsgType.VOICE) {\n      JSONObject voice = new JSONObject();\n      voice.put(\"media_id\", mediaId);\n      obj.put(\"voice\", voice);\n    }\n    if (msgType == MsgType.VIDEO) {\n      JSONObject video = new JSONObject();\n      video.put(\"media_id\", mediaId);\n      video.put(\"thumb_media_id\", thumbMediaId);\n      obj.put(\"video\", video);\n    }\n    if (msgType == MsgType.MUSIC) {\n      JSONObject music = new JSONObject();\n      music.put(\"title\", title);\n      music.put(\"description\", description);\n      music.put(\"musicurl\", musicUrl);\n      music.put(\"hqmusicurl\", hqMusicUrl);\n      music.put(\"thumb_media_id\", thumbMediaId);\n      obj.put(\"music\", music);\n    }\n    if (msgType == MsgType.NEWS) {\n      JSONObject news = new JSONObject();\n      JSONArray array = new JSONArray();\n      for (Article article : articles) {\n        JSONObject item = new JSONObject();\n        item.put(\"title\", article.getTitle());\n        item.put(\"description\", article.getDescription());\n        item.put(\"picurl\", article.getPicUrl());\n        item.put(\"url\", article.getUrl());\n        array.put(item);\n      }\n      news.put(\"articles\", array);\n      obj.put(\"news\", news);\n    }\n    if (msgType == MsgType.TEMPLATE) {\n      obj.put(\"template_id\", templateId);\n      if (StringUtils.isNotBlank(topColor)) {\n        obj.put(\"topcolor\", topColor);\n      }\n      if (StringUtils.isNotBlank(url)) {\n        obj.put(\"url\", url);\n      }\n      JSONObject data = new JSONObject();\n      for (Variable var : variables) {\n        JSONObject varJson = new JSONObject();\n        varJson.put(\"value\", var.getValue());\n        if (StringUtils.isNotBlank(var.getColor())) {\n          varJson.put(\"color\", var.getColor());\n        }\n        data.put(var.getName(), varJson);\n      }\n      obj.put(\"data\", data);\n    }\n    return obj.toString();\n  }\n\n  public static Message parse(String xml) {\n    return parse(XML.toJSONObject(xml).getJSONObject(\"xml\"));\n  }\n\n  public static Message parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    Message obj = new Message(jsonObject);\n    obj.msgId = Result.parseLong(jsonObject.opt(\"MsgId\"));\n    MsgType type = MsgType.parse(jsonObject.get(\"MsgType\"));\n    obj.msgType = type;\n    obj.createTime = new Date(Result.parseLong(jsonObject.get(\"CreateTime\")) * 1000);\n    obj.fromUser = Result.toString(jsonObject.get(\"FromUserName\"));\n    obj.toUser = Result.toString(jsonObject.get(\"ToUserName\"));\n    if (type == MsgType.TEXT) {\n      obj.content = Result.toString(jsonObject.opt(\"Content\"));\n    }\n    if (type == MsgType.IMAGE) {\n      obj.mediaId = Result.toString(jsonObject.opt(\"MediaId\"));\n      obj.picUrl = Result.toString(jsonObject.opt(\"PicUrl\"));\n    }\n    if (type == MsgType.VOICE) {\n      obj.mediaId = Result.toString(jsonObject.opt(\"MediaId\"));\n      obj.voiceType = VoiceType.parse(jsonObject.opt(\"Format\"));\n      obj.recognition = Result.toString(jsonObject.opt(\"Recognition\"));\n    }\n    if (type == MsgType.VIDEO) {\n      obj.mediaId = Result.toString(jsonObject.opt(\"MediaId\"));\n      obj.thumbMediaId = Result.toString(jsonObject.opt(\"ThumbMediaId\"));\n    }\n    if (type == MsgType.LOCATION) {\n      obj.lon = Result.parseDouble(jsonObject.opt(\"Location_Y\"));\n      obj.lat = Result.parseDouble(jsonObject.opt(\"Location_X\"));\n      obj.scale = Result.parseInteger(jsonObject.opt(\"Scale\"));\n      obj.label = Result.toString(jsonObject.opt(\"Label\"));\n    }\n    if (type == MsgType.LINK) {\n      obj.title = Result.toString(jsonObject.opt(\"Title\"));\n      obj.description = Result.toString(jsonObject.opt(\"Description\"));\n      obj.url = Result.toString(jsonObject.opt(\"Url\"));\n    }\n    if (type == MsgType.EVENT) {\n      EventType eventType = EventType.parse(jsonObject.get(\"Event\"));\n      obj.eventType = eventType;\n      if (eventType == EventType.SUBSCRIBE) {\n        // Do nothing when direct follow\n        // When scan QR to follow\n        obj.eventKey = Result.toString(jsonObject.opt(\"EventKey\"));\n        obj.ticket = Result.toString(jsonObject.opt(\"Ticket\"));\n      }\n      if (eventType == EventType.UNSUBSCRIBE) {\n        // Do nothing\n      }\n      if (eventType == EventType.SCAN) {\n        obj.eventKey = Result.toString(jsonObject.opt(\"EventKey\"));\n        obj.ticket = Result.toString(jsonObject.opt(\"Ticket\"));\n      }\n      if (eventType == EventType.LOCATION) {\n        obj.lon = Result.parseDouble(jsonObject.opt(\"Longitude\"));\n        obj.lat = Result.parseDouble(jsonObject.opt(\"Latitude\"));\n      }\n      if (eventType == EventType.CLICK) {\n        obj.eventKey = Result.toString(jsonObject.opt(\"EventKey\"));\n      }\n      if (eventType == EventType.VIEW) {\n        obj.eventKey = Result.toString(jsonObject.opt(\"EventKey\"));\n        obj.url = obj.eventKey;\n      }\n    }\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/MsgType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 普通消息\n */\npublic enum MsgType {\n\n  /**\n   * 文本消息\n   */\n  TEXT(\"text\"),\n\n  /**\n   * 图片消息\n   */\n  IMAGE(\"image\"),\n\n  /**\n   * 语音消息\n   */\n  VOICE(\"voice\"),\n\n  /**\n   * 视频消息\n   */\n  VIDEO(\"video\"),\n\n  /**\n   * 视频消息\n   */\n  SHORTVIDEO(\"shortvideo\"),\n\n  /**\n   * 音乐消息\n   */\n  MUSIC(\"music\"),\n\n  /**\n   * 地理位置消息\n   */\n  LOCATION(\"location\"),\n\n  /**\n   * 链接消息\n   */\n  LINK(\"link\"),\n\n  /**\n   * 事件\n   */\n  EVENT(\"event\"),\n\n  /**\n   * 图文消息\n   */\n  NEWS(\"news\"),\n\n  /**\n   * 模板消息\n   */\n  TEMPLATE(\"template\"),\n\n  /**\n   * 多客服接入\n   */\n  TRANSFER_CUSTOMER_SERVICE(\"transfer_customer_service\");\n\n  private String type;\n\n  private MsgType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public static MsgType parse(Object val) {\n    if (TEXT.type.equals(val)) {\n      return TEXT;\n    }\n    if (IMAGE.type.equals(val)) {\n      return IMAGE;\n    }\n    if (VOICE.type.equals(val)) {\n      return VOICE;\n    }\n    if (VIDEO.type.equals(val)) {\n      return VIDEO;\n    }\n    if (MUSIC.type.equals(val)) {\n      return MUSIC;\n    }\n    if (LOCATION.type.equals(val)) {\n      return LOCATION;\n    }\n    if (LINK.type.equals(val)) {\n      return LINK;\n    }\n    if (EVENT.type.equals(val)) {\n      return EVENT;\n    }\n    if (NEWS.type.equals(val)) {\n      return NEWS;\n    }\n    if (TEMPLATE.type.equals(val)) {\n      return TEMPLATE;\n    }\n    if (TRANSFER_CUSTOMER_SERVICE.type.equals(val)) {\n      return TRANSFER_CUSTOMER_SERVICE;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/QRCreation.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\npublic class QRCreation {\n\n  private QRType type;// 二维码类型，QR_SCENE为临时,QR_LIMIT_SCENE为永久\n  private Integer expireSeconds = 1800;// 该二维码有效时间，以秒为单位。 最大不超过1800。\n  private Integer sceneId;// 场景值ID，临时二维码时为32位整型，永久二维码时最大值为1000\n\n  public QRType getType() {\n    return type;\n  }\n\n  public void setType(QRType type) {\n    this.type = type;\n  }\n\n  public Integer getExpireSeconds() {\n    return expireSeconds;\n  }\n\n  public void setExpireSeconds(Integer expireSeconds) {\n    this.expireSeconds = expireSeconds;\n  }\n\n  public Integer getSceneId() {\n    return sceneId;\n  }\n\n  public void setSceneId(Integer sceneId) {\n    this.sceneId = sceneId;\n  }\n\n  @Override\n  public String toString() {\n    JSONObject obj = new JSONObject();\n    if (type == QRType.QR_SCENE) {\n      obj.put(\"expire_seconds\", expireSeconds);\n    }\n\n    obj.put(\"action_name\", type.value());\n    JSONObject actionInfo = new JSONObject();\n    JSONObject scene = new JSONObject();\n    scene.put(\"scene_id\", sceneId);\n    actionInfo.put(\"scene\", scene);\n    obj.put(\"action_info\", actionInfo);\n    return obj.toString();\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/QRTicket.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 二维码 Ticket\n */\npublic class QRTicket extends JsonBean {\n\n  public QRTicket() {}\n\n  private QRTicket(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String ticket;// 获取的二维码ticket，凭借此ticket可以在有效时间内换取二维码。\n  private Integer expireSeconds = 604800;// 该二维码有效时间，以秒为单位。 最大不超过1800。\n\n  /**\n   * 获取的二维码ticket，凭借此ticket可以在有效时间内换取二维码。\n   */\n  public String getTicket() {\n    return ticket;\n  }\n\n  public void setTicket(String ticket) {\n    this.ticket = ticket;\n  }\n\n  /**\n   * 获取二维码ticket后，开发者可用ticket换取二维码图片。请注意，本接口无须登录态即可调用。\n   */\n  public String getQRUrl() {\n    return \"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=\" + ticket;\n  }\n\n  /**\n   * 该二维码有效时间，以秒为单位。 最大不超过1800。\n   */\n  public Integer getExpireSeconds() {\n    return expireSeconds;\n  }\n\n  public void setExpireSeconds(Integer expireSeconds) {\n    this.expireSeconds = expireSeconds;\n  }\n\n  public static QRTicket parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    QRTicket obj = new QRTicket(jsonObject);\n    obj.ticket = jsonObject.getString(\"ticket\");\n    obj.expireSeconds = Result.parseInteger(jsonObject.opt(\"expire_seconds\"));\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/QRType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 带参数的二维码类型\n */\npublic enum QRType {\n\n  /**\n   * 临时二维码\n   */\n  QR_SCENE(\"QR_SCENE\"),\n\n  /**\n   * 永久二维码\n   */\n  QR_LIMIT_SCENE(\"QR_LIMIT_SCENE\");\n\n  private String type;\n\n  private QRType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public static QRType parse(Object val) {\n    if (QR_LIMIT_SCENE.type.equals(val)) {\n      return QR_LIMIT_SCENE;\n    }\n    if (QR_SCENE.type.equals(val)) {\n      return QR_SCENE;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Scope.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 应用授权作用域\n */\npublic enum Scope {\n\n  /**\n   * 不弹出授权页面，直接跳转，只能获取用户openid\n   */\n  SNSAPI_BASE(\"snsapi_base\"),\n\n  /**\n   * 网页应用目前仅填写snsapi_login即可\n   */\n  SNSAPI_LOGIN(\"snsapi_login\"),\n\n  /**\n   * 弹出授权页面，可通过openid拿到昵称、性别、所在地。并且，即使在未关注的情况下，只要用户授权，也能获取其信息\n   */\n  SNSAPI_USERINFO(\"snsapi_userinfo\");\n\n  private String scope;\n\n  private Scope(String scope) {\n    this.scope = scope;\n  }\n\n  public String value() {\n    return scope;\n  }\n\n  @Override\n  public String toString() {\n    return scope;\n  }\n\n  public static Scope parse(Object val) {\n    if (SNSAPI_BASE.scope.equals(val)) {\n      return SNSAPI_BASE;\n    }\n    if (SNSAPI_USERINFO.scope.equals(val)) {\n      return SNSAPI_USERINFO;\n    }\n    if (SNSAPI_LOGIN.scope.equals(val)) {\n      return SNSAPI_LOGIN;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/User.java",
    "content": "package com.belerweb.social.weixin.bean;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.json.JSONObject;\n\nimport com.belerweb.social.bean.Gender;\nimport com.belerweb.social.bean.JsonBean;\nimport com.belerweb.social.bean.Result;\n\n/**\n * 微信用户信息\n */\npublic class User extends JsonBean {\n\n  public User() {}\n\n  private User(JSONObject jsonObject) {\n    super(jsonObject);\n  }\n\n  private String openId;// 用户的唯一标识\n  private String unionID;// 同一用户，对同一个微信开放平台帐号下的不同应用，UnionID是相同的\n  private String nickname;// 用户昵称\n  private Gender gender;// 用户的性别，值为1时是男性，值为2时是女性，值为0时是未知\n  private String province;// 用户个人资料填写的省份\n  private String city;// 普通用户个人资料填写的城市\n  private String country;// 国家，如中国为CN\n  private List<String> privilege; // 用户特权信息，json 数组，如微信沃卡用户为（chinaunicom）\n  private String language;// 用户的语言，简体中文为zh_CN\n  private String headImgUrl;// 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空\n  private Boolean subscribe;// 用户是否订阅该公众号标识，值为0时，代表此用户没有关注该公众号，拉取不到其余信息。\n  private Date subscribeTime;// 用户关注时间，为时间戳。如果用户曾多次关注，则取最后关注时间\n\n  /**\n   * 用户的唯一标识\n   */\n  public String getOpenId() {\n    return openId;\n  }\n\n  public void setOpenId(String openId) {\n    this.openId = openId;\n  }\n\n  /**\n   * 用户昵称\n   */\n  public String getNickname() {\n    return nickname;\n  }\n\n  public void setNickname(String nickname) {\n    this.nickname = nickname;\n  }\n\n  /**\n   * 用户的性别\n   */\n  public Gender getGender() {\n    return gender;\n  }\n\n  public void setGender(Gender gender) {\n    this.gender = gender;\n  }\n\n  /**\n   * 用户个人资料填写的省份\n   */\n  public String getProvince() {\n    return province;\n  }\n\n  public void setProvince(String province) {\n    this.province = province;\n  }\n\n  /**\n   * 普通用户个人资料填写的城市\n   */\n  public String getCity() {\n    return city;\n  }\n\n  public void setCity(String city) {\n    this.city = city;\n  }\n\n  /**\n   * 国家，如中国为CN\n   */\n  public String getCountry() {\n    return country;\n  }\n\n  public void setCountry(String country) {\n    this.country = country;\n  }\n\n  /**\n   * 用户特权信息，json 数组，如微信沃卡用户为（chinaunicom）\n   */\n  public List<String> getPrivilege() {\n    return privilege;\n  }\n\n  public void setPrivilege(List<String> privilege) {\n    this.privilege = privilege;\n  }\n\n  /**\n   * 用户的语言，简体中文为zh_CN\n   */\n  public String getLanguage() {\n    return language;\n  }\n\n  public void setLanguage(String language) {\n    this.language = language;\n  }\n\n  /**\n   * 用户头像，最后一个数值代表正方形头像大小（有0、46、64、96、132数值可选，0代表640*640正方形头像），用户没有头像时该项为空\n   */\n  public String getHeadImgUrl() {\n    return headImgUrl;\n  }\n\n  public void setHeadImgUrl(String headImgUrl) {\n    this.headImgUrl = headImgUrl;\n  }\n\n  /**\n   * 用户是否订阅该公众号标识，false 表此用户没有关注该公众号，拉取不到其余信息。\n   */\n  public Boolean getSubscribe() {\n    return subscribe;\n  }\n\n  public void setSubscribe(Boolean subscribe) {\n    this.subscribe = subscribe;\n  }\n\n  /**\n   * 用户关注时间，为时间戳。如果用户曾多次关注，则取最后关注时间\n   */\n  public Date getSubscribeTime() {\n    return subscribeTime;\n  }\n\n  public void setSubscribeTime(Date subscribeTime) {\n    this.subscribeTime = subscribeTime;\n  }\n\n  /**\n   * 公众号只有在被绑定到微信开放平台帐号下后，才会获取UnionID。只要是同一个微信开放平台帐号下的公众号，用户的UnionID是唯一的\n   * \n   * @return the unionId\n   */\n  public String getUnionID() {\n    return unionID;\n  }\n\n  public void setUnionID(String unionID) {\n    this.unionID = unionID;\n  }\n\n  public static User parse(JSONObject jsonObject) {\n    if (jsonObject == null) {\n      return null;\n    }\n    User obj = new User(jsonObject);\n    obj.openId = Result.toString(jsonObject.get(\"openid\"));\n    if (jsonObject.has(\"unionid\")) {\n      obj.unionID = Result.toString(jsonObject.get(\"unionid\"));\n    }\n    obj.nickname = Result.toString(jsonObject.opt(\"nickname\"));\n    obj.gender = Gender.parse(Result.parseInteger(jsonObject.opt(\"sex\")));\n    obj.province = Result.toString(jsonObject.opt(\"province\"));\n    obj.city = Result.toString(jsonObject.opt(\"city\"));\n    obj.country = Result.toString(jsonObject.opt(\"country\"));\n    obj.language = Result.toString(jsonObject.opt(\"language\"));\n    obj.headImgUrl = Result.toString(jsonObject.opt(\"headimgurl\"));\n    obj.subscribe = Result.parseBoolean(jsonObject.opt(\"subscribe\"));\n    Long subscribeTime = Result.parseLong(jsonObject.opt(\"subscribe_time\"));\n    if (subscribeTime != null) {\n      obj.subscribeTime = new Date(subscribeTime * 1000);\n    }\n    obj.privilege = Result.parse(jsonObject.optJSONArray(\"privilege\"), String.class);\n    return obj;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/Variable.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n/**\n * 模板消息中的变量\n * \n * @date Aug 28, 2014\n */\npublic class Variable {\n\n  private String name;// 变量名称\n  private String value;// 变量值\n  private String color;// 变量颜色值 eg:#FF0000\n\n  public Variable() {}\n\n  public Variable(String name, String value) {\n    this.name = name;\n    this.value = value;\n  }\n\n  public Variable(String name, String value, String color) {\n    this.name = name;\n    this.value = value;\n    this.color = color;\n  }\n\n  /**\n   * 变量名称\n   */\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  /**\n   * 变量值\n   */\n  public String getValue() {\n    return value;\n  }\n\n  public void setValue(String value) {\n    this.value = value;\n  }\n\n  /**\n   * 变量颜色值 eg:#FF0000\n   */\n  public String getColor() {\n    return color;\n  }\n\n  public void setColor(String color) {\n    this.color = color;\n  }\n\n}\n"
  },
  {
    "path": "src/main/java/com/belerweb/social/weixin/bean/VoiceType.java",
    "content": "package com.belerweb.social.weixin.bean;\n\n\n/**\n * 语音格式\n */\npublic enum VoiceType {\n\n  /**\n   * amr\n   */\n  AMR(\"amr\"),\n\n  /**\n   * speex\n   */\n  SPEEX(\"speex\");\n\n  private String type;\n\n  private VoiceType(String type) {\n    this.type = type;\n  }\n\n  public String value() {\n    return type;\n  }\n\n  @Override\n  public String toString() {\n    return type;\n  }\n\n  public static VoiceType parse(Object val) {\n    if (AMR.type.equals(val)) {\n      return AMR;\n    }\n    if (SPEEX.type.equals(val)) {\n      return SPEEX;\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/SDKTest.java",
    "content": "package com.belerweb.social;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.bean.Result;\n\npublic class SDKTest extends TestConfig {\n\n  final static Logger logger = LoggerFactory.getLogger(SDKTest.class);\n\n  @Test\n  public void testLonLatToAddress() {\n    Result<String> result = weibo.lonLatToAddress(118.839485, 31.954561);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/TestConfig.java",
    "content": "package com.belerweb.social;\n\nimport org.junit.Before;\n\nimport com.belerweb.social.qq.connect.api.QQConnect;\nimport com.belerweb.social.weibo.api.Weibo;\nimport com.belerweb.social.weixin.api.Weixin;\n\npublic class TestConfig {\n\n  protected Weibo weibo;\n  protected QQConnect connect;\n  protected Weixin weixin;\n\n  @Before\n  public void initialize() {\n    String redirectUri = System.getProperty(\"redirect\");\n    weibo =\n        new Weibo(System.getProperty(\"weibo.id\"), System.getProperty(\"weibo.secret\"), redirectUri);\n    connect =\n        new QQConnect(System.getProperty(\"connect.id\"), System.getProperty(\"connect.secret\"),\n            redirectUri);\n    weixin =\n        new Weixin(System.getProperty(\"weixin.id\"), System.getProperty(\"weixin.secret\"),\n            redirectUri, System.getProperty(\"weixin.token\"));\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/captcha/api/YundamaTest.java",
    "content": "package com.belerweb.social.captcha.api;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.captcha.bean.YundamaType;\n\npublic class YundamaTest {\n\n  final static Logger logger = LoggerFactory.getLogger(YundamaTest.class);\n\n  @Test\n  public void testDecode() {\n    try {\n      byte[] img =\n          new byte[] {-1, -40, -1, -32, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, -1,\n              -2, 0, 59, 67, 82, 69, 65, 84, 79, 82, 58, 32, 103, 100, 45, 106, 112, 101, 103, 32,\n              118, 49, 46, 48, 32, 40, 117, 115, 105, 110, 103, 32, 73, 74, 71, 32, 74, 80, 69, 71,\n              32, 118, 54, 50, 41, 44, 32, 113, 117, 97, 108, 105, 116, 121, 32, 61, 32, 56, 48,\n              10, -1, -37, 0, 67, 0, 6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, 10, 10, 9,\n              9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20, 22, 22, 26, 29, 37, 31, 26, 27,\n              35, 28, 22, 22, 32, 44, 32, 35, 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37,\n              40, 41, 40, -1, -37, 0, 67, 1, 7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26,\n              40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,\n              40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,\n              40, 40, 40, 40, 40, 40, 40, 40, -1, -64, 0, 17, 8, 0, 53, 0, -126, 3, 1, 34, 0, 2,\n              17, 1, 3, 17, 1, -1, -60, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,\n              0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -60, 0, -75, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5,\n              5, 4, 4, 0, 0, 1, 125, 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34,\n              113, 20, 50, -127, -111, -95, 8, 35, 66, -79, -63, 21, 82, -47, -16, 36, 51, 98, 114,\n              -126, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58,\n              67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102,\n              103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, -125, -124, -123, -122,\n              -121, -120, -119, -118, -110, -109, -108, -107, -106, -105, -104, -103, -102, -94,\n              -93, -92, -91, -90, -89, -88, -87, -86, -78, -77, -76, -75, -74, -73, -72, -71, -70,\n              -62, -61, -60, -59, -58, -57, -56, -55, -54, -46, -45, -44, -43, -42, -41, -40, -39,\n              -38, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -15, -14, -13, -12, -11, -10,\n              -9, -8, -7, -6, -1, -60, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,\n              1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, -1, -60, 0, -75, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5,\n              4, 4, 0, 1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34,\n              50, -127, 8, 20, 66, -111, -95, -79, -63, 9, 35, 51, 82, -16, 21, 98, 114, -47, 10,\n              22, 36, 52, -31, 37, -15, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58,\n              67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102,\n              103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, -126, -125, -124, -123,\n              -122, -121, -120, -119, -118, -110, -109, -108, -107, -106, -105, -104, -103, -102,\n              -94, -93, -92, -91, -90, -89, -88, -87, -86, -78, -77, -76, -75, -74, -73, -72, -71,\n              -70, -62, -61, -60, -59, -58, -57, -56, -55, -54, -46, -45, -44, -43, -42, -41, -40,\n              -39, -38, -30, -29, -28, -27, -26, -25, -24, -23, -22, -14, -13, -12, -11, -10, -9,\n              -8, -7, -6, -1, -38, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, -6, 122, 40, -48, -58,\n              -124, -94, -25, 3, -75, 59, -53, 79, -18, 47, -27, 68, 95, -22, -109, -3, -47, 89,\n              -38, -34, -65, -91, 104, 104, -115, -86, -34, -59, 111, -65, -18, 43, 100, -77, 125,\n              20, 100, -97, -64, 87, 27, 113, -118, -69, 51, -47, 45, 77, 31, 45, 63, -72, -65,\n              -107, 87, 75, -101, 39, 55, 1, 38, -74, 99, 110, 113, 48, 12, -89, -54, 56, -49, -51,\n              -23, -57, 60, -43, 53, -15, 38, -109, -27, -93, -49, 120, -74, -117, 32, 5, 13, -30,\n              53, -74, -4, -25, -18, -7, -127, 115, -45, -73, -88, -11, 21, -105, -29, 41, 68, -63,\n              116, -69, 72, -82, 35, -44, 103, 97, 115, 109, 40, -120, -7, 50, 75, 24, 46, 17, -33,\n              -95, 36, 70, 70, 15, 56, -26, -90, 83, -118, 87, 90, -125, 106, -38, 23, 116, 111,\n              17, -23, 90, -58, -87, 117, 97, 100, 36, 105, 109, -48, 72, 89, -94, 42, -110, 41,\n              -29, 114, -109, -44, 103, -65, 126, -39, -83, -65, 45, 63, -72, -65, -107, 115, 66,\n              -14, 23, -69, -46, 60, 65, 2, -19, -126, -14, 53, -76, -72, -36, 57, 77, -57, 41,\n              -97, 117, -109, 40, 127, -33, -10, -82, -94, -118, 110, -21, 80, -120, -49, 45, 63,\n              -72, -65, -107, 30, 92, 127, -36, 95, -54, -80, -4, 111, -30, 40, 124, 49, -31, -7,\n              -17, -28, -38, -45, 125, -56, 35, 63, -57, 33, -24, 62, -99, -49, -80, -81, 5, -69,\n              -15, 95, -119, -19, 116, -53, -72, 38, -71, -107, 23, 80, -105, -49, -106, 113, -9,\n              -40, -107, 3, 104, 63, -62, 48, 7, 3, 29, 49, -46, -80, -60, 98, -31, 65, -14, -75,\n              114, 39, 81, 67, 67, -24, -85, 29, 67, 79, -65, -72, -70, -126, -50, 88, 102, -110,\n              -43, -126, 77, -80, 100, 35, 30, -39, -23, -102, 124, 55, 118, 51, 94, -49, 103, 20,\n              -42, -17, 117, 6, 60, -40, 65, 27, -45, 32, 17, -111, -41, -95, 21, -29, 127, 8, -68,\n              67, -89, -8, 107, -61, -102, -107, -34, -86, -14, 36, 50, -35, -92, 74, 81, 11, 124,\n              -37, 9, -24, 62, -122, -71, 31, 19, -36, -22, 55, -66, 33, -44, 124, 73, -89, 71,\n              121, 21, -101, 92, -2, -26, -15, 85, -112, 1, -47, 112, -35, -114, 0, -84, 94, 57,\n              42, 113, -99, -82, -34, -21, -56, -105, 85, 36, -99, -113, -93, -76, -3, 67, 79, -44,\n              38, -69, -122, -46, 88, -92, -106, -42, 67, 20, -56, 7, 40, -61, -44, 127, 90, -69,\n              -27, -89, -9, 23, -14, -81, 14, -75, -48, 124, 67, -32, -55, -93, -15, 118, -91, 121,\n              27, -17, -71, 67, 121, 20, 78, 92, -68, 114, 55, -52, 88, -114, 15, 36, 116, -49, 36,\n              30, -43, -18, 72, -54, -24, -84, -124, 50, -80, -56, 35, -95, 21, -45, 66, -85, -88,\n              -102, -100, 108, -1, 0, 66, -31, 46, 109, -48, -98, 90, 127, 113, 127, 42, 60, -76,\n              -2, -30, -2, 84, 75, 34, -59, 19, -55, 33, -38, -120, 11, 49, -12, 2, -71, -81, 10,\n              -8, -25, 69, -15, 52, -83, 13, -116, -17, 29, -46, -25, -9, 19, -128, -82, -61, -43,\n              121, -63, -2, 117, -77, -108, 34, -44, 91, -43, -108, -38, 78, -57, 75, -27, -89, -9,\n              23, -14, -93, -53, 79, -18, 47, -27, 79, -94, -86, -56, 118, 51, 101, 24, -111, -64,\n              -23, -109, 69, 18, -1, 0, -83, 127, -9, -115, 21, -54, -9, 48, 101, -8, -65, -43, 39,\n              -5, -94, -71, 31, -120, 90, 71, -121, 111, 18, -46, -9, -60, 119, 109, 102, -48, 110,\n              72, 38, 89, 54, -112, -57, -98, -35, 72, -58, 107, -82, -117, -3, 82, 127, -70, 42,\n              -114, -73, -94, -23, -6, -27, -76, 118, -6, -83, -78, -36, -61, 28, -126, 85, 70, 36,\n              0, -64, 17, -98, 15, -95, 53, -75, 72, 115, -61, -106, -55, -6, -101, 53, 117, 99,\n              58, 38, -77, -41, 60, 47, 34, 120, -122, 40, 93, 34, 13, 21, -30, -65, -35, 71, 78,\n              25, -77, -40, 127, 16, 62, -124, 26, -53, -70, -67, -42, 124, 38, -119, 36, -120, -6,\n              -41, -121, -64, 31, -66, 78, 110, 109, -45, -43, -79, -60, -118, 7, 126, 15, -83,\n              107, -68, 81, -23, -2, 32, -14, -39, 7, -40, 117, 88, -4, -74, 83, -9, 68, -56, -68,\n              127, -33, 81, -126, 63, -19, -104, -11, -82, 67, 72, -101, 90, -16, -66, -83, 6,\n              -115, -91, 105, -70, -122, -89, 100, -110, -55, 28, -86, -33, -22, -29, 78, 30, 54,\n              73, 27, 10, -89, 99, 96, -87, 56, -56, -29, 4, -42, 21, 27, -115, -81, -66, -41, 95,\n              -27, -42, -28, 55, 99, 87, 69, -15, 14, -105, -30, 93, 87, 93, -16, -20, 66, 15, -78,\n              -19, -13, 97, -106, -33, -27, -13, 85, -44, 23, 111, -9, -61, -79, 57, -9, -23, -112,\n              73, -22, -84, 110, -102, -30, 121, 18, 22, 73, 109, -96, -52, 79, 35, 49, 18, -7,\n              -86, 112, 65, 93, -96, 99, 24, 57, -49, 57, -32, 99, 6, -71, -120, 124, 39, -94, -65,\n              -118, 34, -15, 6, -127, 114, -79, -49, 108, -51, -10, -120, 44, -39, 25, 101, 37, 79,\n              -54, 70, 112, -92, -28, 103, -41, -40, -13, 80, -4, 73, -42, 33, -16, -59, -101, -52,\n              -6, 60, -105, -42, -38, -95, 49, 93, 50, 93, 60, 32, 16, -96, 0, 112, 15, 37, 65, 25,\n              24, 56, 90, 113, -100, -87, -63, -50, -91, -76, -4, -70, 127, -112, 38, -30, -81, 35,\n              -109, -8, -121, 14, -79, -85, -8, -78, 73, 47, 108, 110, -96, -46, 116, -36, 44, 12,\n              -56, 76, 110, 78, 62, 125, -61, -114, 78, 63, 32, 14, 14, 107, -100, -43, 116, -58,\n              -44, -30, -119, 12, -62, 56, -112, -27, -126, -82, 114, 123, 119, -12, 53, -48, 104,\n              127, 18, 97, -79, -80, -74, -80, -47, 108, 45, -84, -83, -43, -10, 71, 109, 43, 73,\n              41, 66, -60, -110, 119, -110, 50, 55, 19, -12, -51, 95, -44, 124, 85, 107, -85, 94,\n              -38, -40, 77, -31, 43, 61, 70, -13, 80, -3, -53, 73, -26, -124, 36, -32, 12, -18,\n              -40, 89, 112, 57, -50, 114, 0, -21, -59, 121, -14, 84, -86, 94, 92, -38, -65, 95,\n              -21, -16, 50, 124, -78, -42, -25, 5, -31, -97, 4, -34, 107, -79, -21, 50, 66, -52,\n              32, -45, -47, -10, 16, 63, -42, -54, 6, 66, 15, -64, 115, -8, 122, -41, -81, -39, 50,\n              -8, -21, -31, 103, -105, -63, -71, -102, -37, -53, 62, -45, 39, 79, -90, 89, 65, -6,\n              26, -22, -12, 77, 38, -53, 68, -45, -46, -53, 76, -73, 91, 123, 100, 37, -126, 6, 45,\n              -55, -28, -110, 79, 39, -15, -84, -33, 12, -23, 26, 95, -121, -82, -17, -84, 52, -21,\n              -62, 101, -71, -112, -34, 53, -93, -54, -124, -60, 9, -63, 42, -96, 2, 23, -96, -25,\n              61, 7, 53, -41, 71, 9, -20, 116, -24, -43, -97, -24, 105, 26, 124, -70, 119, 42, -40,\n              104, -41, -73, -33, 14, 19, 71, -42, -111, 18, -11, -20, -52, 12, 3, 110, -38, 64,\n              -62, 18, 125, 70, 20, -97, 122, -121, -31, 70, -84, -38, -89, -125, -83, -93, -100,\n              -97, -75, 88, -109, 105, 40, 61, 65, 78, -97, -8, -18, 63, 28, -45, -17, -68, 65,\n              109, -31, -1, 0, 17, -35, -82, -83, 38, -91, 29, -108, -15, -122, 23, 87, 42, 62,\n              -53, 27, 14, -111, -59, -75, 114, 88, -126, -60, -25, -97, -112, 117, -81, 62, 111,\n              29, -61, 101, -82, -33, -89, -127, 52, -85, 98, 110, -27, 18, 75, 37, -61, 74, -58,\n              -23, -53, 0, 54, 71, -111, -80, -18, 118, -23, -44, 122, 98, -100, -86, -62, -108,\n              -94, -17, -74, -115, 117, -14, 7, 37, 22, -114, -49, 93, -44, 53, 93, 123, 82, -46,\n              -49, -125, 53, 80, 44, -29, -72, -110, -34, -10, 68, -117, 114, 33, 0, 18, 73, 35, 7,\n              -125, -64, -11, 35, -16, 93, 95, 71, -16, -25, -107, -91, -92, 118, -112, -36, -53,\n              14, -96, -106, -119, 45, -76, -31, 36, 18, 96, -18, -13, 36, 95, -104, 17, -13, 49,\n              0, -28, -80, 94, -103, 53, 22, -67, -84, 78, -70, 77, -58, -116, -38, -2, -97, 109,\n              -30, 123, -124, 12, -30, 103, 49, -57, 10, -74, 78, -60, 96, 49, -112, 14, 1, 63, 49,\n              -21, -23, 89, 122, 111, -125, -91, -97, -62, 22, 26, 46, -93, 23, -40, -124, -38,\n              -104, -107, -4, -87, 67, -18, 85, -124, -110, -54, -61, -5, -59, 78, 61, 51, -19, 74,\n              77, -54, 77, 37, 118, -2, -27, -27, -42, -34, 96, -11, 125, -49, 84, -94, -103, 18,\n              44, 81, -92, 104, 48, -86, 2, -114, 115, -64, -89, -41, -96, 108, 102, -53, -2, -75,\n              -1, 0, -34, 52, 81, 47, -6, -41, -1, 0, 120, -47, 92, -113, 115, -99, -105, -30, -1,\n              0, 84, -97, -18, -118, 125, 50, 47, -11, 73, -2, -24, -89, -41, 82, -40, -35, 108,\n              114, -33, 17, 117, -69, 125, 3, 64, -114, -6, -22, -59, -81, 66, -36, -57, -78, 53,\n              -112, -57, -75, -63, -36, 27, 112, 7, 24, -37, -8, -12, -17, 89, -66, 37, -15, 52,\n              -10, -66, 30, -45, 124, 87, -91, -57, -10, -69, 22, 64, 46, 109, 89, -56, 93, -84,\n              70, 27, -72, 12, -82, 54, -25, 29, -49, -96, -82, -34, -30, 8, -82, 97, 104, -82, 34,\n              73, 98, 97, -122, 71, 80, -54, 126, -96, -41, 13, -84, -8, 91, 90, -5, 53, -26, -113,\n              -96, 79, -89, 91, 104, 87, -123, -99, -60, -56, 89, -31, -35, -9, -111, 20, 113, -76,\n              -100, -80, -12, -55, -58, 48, 43, -102, -76, 106, 43, -72, -11, 93, 55, -71, 18, 79,\n              -96, -49, 27, -37, 107, 51, -35, -24, 122, -65, -124, 109, -103, 47, -90, 33, 39,\n              -112, -100, 15, 44, -128, 85, 101, 29, 10, -11, -25, -73, 110, 72, -91, -15, -75,\n              -89, -120, -11, -3, 30, 61, 20, -24, -42, -37, 110, 29, 5, -59, -22, 92, -87, 72,\n              -80, -32, -18, 69, 108, 55, 65, -6, -111, -49, 90, -18, -32, 67, 28, 17, -93, 16, 74,\n              -88, 82, 71, -80, -87, 42, -98, 29, 75, -102, -19, -21, -72, -36, 47, 127, 51, -25,\n              -97, -120, -66, 1, -73, -16, -114, -109, 105, 119, 5, -20, -41, 50, 75, 55, -106,\n              -37, -108, 40, 31, 41, 60, 99, -23, -21, 93, -73, -63, -81, 7, 73, -91, -37, 54, -75,\n              -87, -57, -74, -10, -27, 49, 2, 48, -26, 56, -49, 115, -24, 79, -24, 62, -90, -69,\n              -19, 99, 70, -77, -42, 13, -105, -37, -29, -13, 82, -42, 113, 112, -120, 126, -23,\n              96, -92, 12, -114, -29, -100, -29, -40, 86, -107, 99, 79, 3, 8, 85, -10, -117, 110,\n              -120, -120, -46, 74, 87, 10, -32, 62, 40, -8, 107, 82, -44, 22, -33, 89, -16, -20,\n              -78, -59, -85, 89, -93, 71, -120, -101, 107, -55, 27, 117, 10, 125, 70, 79, 29, -14,\n              123, -30, -69, -6, 43, -86, -83, 53, 86, 46, 44, -46, 81, -26, 86, 103, -119, -22,\n              -87, -29, 95, 6, 94, 105, -59, -75, -69, -85, -69, 59, -26, -118, 57, 100, -108, -7,\n              -62, 57, 59, -89, -49, -100, 117, 56, 35, 25, -4, 43, -47, 111, -76, 56, -19, -4, 69,\n              109, -86, 88, 104, 90, 108, -46, 41, 37, -26, 71, -14, 38, 70, 114, 124, -57, 32, 13,\n              -78, 18, 49, -116, -112, 65, -49, 63, 55, 29, 13, -35, -76, 55, 112, 52, 55, 49, 44,\n              -79, 49, 4, -85, 12, -116, -125, -112, 127, 2, 1, -87, -85, 40, 97, -108, 27, 87,\n              -70, -23, -42, -33, 121, 42, 22, 57, -117, -65, 8, -39, -99, 38, -6, 61, 59, -3, 23,\n              83, -69, 83, -69, 80, 117, 18, 76, 92, -14, 73, 111, 126, -104, 24, 3, 60, 99, -118,\n              -27, -84, 60, 31, 117, 7, -123, 109, -12, 75, -69, -119, -90, -113, -5, 95, 119, -38,\n              108, -33, -26, 84, -39, -126, -64, -13, -73, 15, -72, 28, -9, -51, 122, -123, 99, 39,\n              -122, -12, -24, -75, -10, -42, 96, 73, -95, -67, 127, -11, -66, 84, -84, -87, 47, 24,\n              -7, -108, 28, 31, -15, -26, -118, -104, 120, -74, -102, 94, 95, 32, 112, 76, -47,\n              -45, -19, -123, -107, -123, -75, -86, -55, 36, -94, 8, -42, 48, -14, 28, -77, 109,\n              24, -53, 30, -28, -29, -102, -79, 69, 21, -48, -107, -76, 52, 51, 101, -1, 0, 90, -1,\n              0, -17, 26, 40, -105, -3, 107, -1, 0, -68, 104, -82, 87, -71, -50, -55, -110, -21,\n              106, -127, -77, -96, -57, 90, 119, -38, -1, 0, -40, -3, 104, -94, -85, -98, 69, 115,\n              48, -5, 95, -5, 31, -83, 31, 107, -1, 0, 99, -11, -94, -118, 61, -92, -125, -99,\n              -121, -38, -1, 0, -40, -3, 104, -5, 95, -5, 31, -83, 20, 81, -19, 36, 28, -20, 62,\n              -41, -2, -57, -21, 71, -38, -1, 0, -40, -3, 104, -94, -113, 105, 32, -25, 97, -10,\n              -65, -10, 63, 90, 62, -41, -2, -57, -21, 69, 20, 123, 73, 7, 59, 15, -75, -1, 0, -79,\n              -6, -47, -10, -65, -10, 63, 90, 40, -93, -38, 72, 57, -40, 125, -81, -3, -113, -42,\n              -113, -75, -1, 0, -79, -6, -47, 69, 30, -46, 65, -50, -61, -19, 127, -20, 126, -76,\n              125, -81, -3, -113, -42, -118, 40, -10, -110, 14, 118, 86, 115, -71, -119, -11, 57,\n              -94, -118, 42, 73, 63, -1, -39,};\n      Result<String> result =\n          new Yundama(System.getProperty(\"yundama.username\"),\n              System.getProperty(\"yundama.password\")).decode(img, YundamaType.ALPHABETIC4);\n      if (result.success()) {\n        logger.info(\"Text:{}\", result.getResult());\n      } else {\n        logger.error(\"Error:{}\", result.getError().getErrorCode());\n      }\n      Assert.assertTrue(\"nkha\".equalsIgnoreCase(result.getResult()));\n    } catch (Exception e) {\n      e.printStackTrace();\n      Assert.fail(e.getMessage());\n    }\n  }\n\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/mail/api/POP3Test.java",
    "content": "package com.belerweb.social.mail.api;\n\nimport org.junit.Test;\n\nimport com.belerweb.social.mail.api.POP3;\n\npublic class POP3Test {\n\n  @Test\n  public void testDownload() {\n    String username = System.getProperty(\"pop3.username\");\n    String password = System.getProperty(\"pop3.password\");\n    String host = System.getProperty(\"pop3.host\");\n    POP3 pop3 = new POP3(username, password, host, 995, true);\n    pop3.download(System.getProperty(\"java.io.tmpdir\"));\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/qq/connect/api/OAuth2Test.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.qq.connect.bean.AccessToken;\nimport com.belerweb.social.qq.connect.bean.OpenID;\n\npublic class OAuth2Test extends TestConfig {\n\n  final static Logger logger = LoggerFactory.getLogger(OAuth2Test.class);\n\n  @Test\n  public void testAuthorize() {\n    String url = connect.getOAuth2().authorize();\n    // 浏览器打开URL获取code用于下一步测试\n    logger.info(url);\n  }\n\n  @Test\n  public void testAuthorizeWap() {\n    String url = connect.getOAuth2().authorize(true);\n    // 浏览器打开URL获取code用于下一步测试\n    logger.info(url);\n  }\n\n  @Test\n  public void testAccessToken() {\n    Result<AccessToken> tokenResult = connect.getOAuth2().accessToken(\"code\");\n    Assert.assertTrue(!tokenResult.success());\n    logger.info(tokenResult.getError().toString());\n\n    String code = System.getProperty(\"connect.code\");\n    tokenResult = connect.getOAuth2().accessToken(code);\n    Assert.assertTrue(tokenResult.success());\n    logger.info(tokenResult.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testAccessTokenWap() {\n    Result<AccessToken> tokenResult = connect.getOAuth2().accessToken(\"code\", true);\n    Assert.assertTrue(!tokenResult.success());\n    logger.info(tokenResult.getError().toString());\n\n    String code = System.getProperty(\"connect.code\");\n    tokenResult = connect.getOAuth2().accessToken(code, true);\n    Assert.assertTrue(tokenResult.success());\n    logger.info(tokenResult.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testOpenId() {\n    String accessToken = System.getProperty(\"connect.token\");\n    Result<OpenID> result = connect.getOAuth2().openId(accessToken);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testOpenIdWap() {\n    String accessToken = System.getProperty(\"connect.token\");\n    Result<OpenID> result = connect.getOAuth2().openId(accessToken, true);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testRefreshAccessToken() {\n    String refreshToken = System.getProperty(\"connect.rtoken\");\n    Result<AccessToken> result = connect.getOAuth2().refreshAccessToken(refreshToken);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testRefreshAccessTokenWap() {\n    String refreshToken = System.getProperty(\"connect.rtoken\");\n    Result<AccessToken> result = connect.getOAuth2().refreshAccessToken(refreshToken, true);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/qq/connect/api/UserTest.java",
    "content": "package com.belerweb.social.qq.connect.api;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\n\npublic class UserTest extends TestConfig {\n  final static Logger logger = LoggerFactory.getLogger(UserTest.class);\n\n  @Test\n  public void testGetUserInfo() {\n    String openId = System.getProperty(\"connect.openid\");\n    String accessToken = System.getProperty(\"connect.token\");\n\n    Result<com.belerweb.social.qq.connect.bean.User> result =\n        connect.getUser().getUserInfo(accessToken, openId);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testGetSimpleUserInfo() {\n    String openId = System.getProperty(\"connect.openid\");\n    String accessToken = System.getProperty(\"connect.token\");\n\n    Result<com.belerweb.social.qq.connect.bean.User> result =\n        connect.getUser().getSimpleUserInfo(accessToken, connect.getClientId(), openId);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weibo/api/OAuth2Test.java",
    "content": "package com.belerweb.social.weibo.api;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weibo.bean.AccessToken;\nimport com.belerweb.social.weibo.bean.TokenInfo;\n\npublic class OAuth2Test extends TestConfig {\n\n  final static Logger logger = LoggerFactory.getLogger(OAuth2Test.class);\n\n  /**\n   * 获取用于获取accessToken的code，此方法需要单独测试\n   */\n  @Test\n  public void testAuthorize() {\n    String url = weibo.getOAuth2().authorize();\n    // 浏览器打开URL获取code用于下一步测试\n    logger.info(url);\n  }\n\n  /**\n   * 获取accessToken，此方法需要单独测试\n   */\n  @Test\n  public void testAccessToken() {\n    Result<AccessToken> tokenResult = weibo.getOAuth2().accessToken(\"code\");\n    Assert.assertTrue(!tokenResult.success());\n    logger.info(tokenResult.getError().toString());\n\n    String code = System.getProperty(\"weibo.code\");\n    tokenResult = weibo.getOAuth2().accessToken(code);\n    Assert.assertTrue(tokenResult.success());\n    logger.info(tokenResult.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testGetTokenInfo() {\n    String accessToken = System.getProperty(\"weibo.token\");\n    Result<TokenInfo> tokenResult = weibo.getOAuth2().getTokenInfo(accessToken);\n    Assert.assertTrue(tokenResult.success());\n    logger.info(tokenResult.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testRevokeOAuth2() {\n    String accessToken = System.getProperty(\"weibo.token\");\n    Result<Boolean> tokenResult = weibo.getOAuth2().revokeOAuth2(accessToken);\n    Assert.assertTrue(tokenResult.success());\n    Assert.assertTrue(tokenResult.getResult());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weibo/api/UserTest.java",
    "content": "package com.belerweb.social.weibo.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weibo.bean.UserCounts;\n\npublic class UserTest extends TestConfig {\n\n  final static Logger logger = LoggerFactory.getLogger(UserTest.class);\n\n  @Test\n  public void testShow() {\n    String uid = System.getProperty(\"weibo.uid\");\n    Result<com.belerweb.social.weibo.bean.User> result =\n        weibo.getUser().show(weibo.getClientId(), null, uid, null);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testDomainShow() {\n    Result<com.belerweb.social.weibo.bean.User> result =\n        weibo.getUser().domainShow(weibo.getClientId(), null, \"cqlybest\");\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testCounts() {\n    String accessToken = System.getProperty(\"weibo.token\");\n    String uid = System.getProperty(\"weibo.uid\");\n    List<String> uids = new ArrayList<String>();\n    uids.add(uid);\n    Result<UserCounts> result = weibo.getUser().counts(null, accessToken, uids);\n    Assert.assertTrue(result.success());\n    List<UserCounts> results = result.getResults();\n    for (UserCounts userCounts : results) {\n      logger.info(userCounts.getJsonObject().toString());\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/GroupTest.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.util.List;\n\nimport org.apache.commons.lang.RandomStringUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.Group;\n\npublic class GroupTest extends TestConfig {\n  final static Logger logger = LoggerFactory.getLogger(GroupTest.class);\n\n  @Test\n  public void testCreate() {\n    Result<Group> result = weixin.getGroup().create(RandomStringUtils.randomAlphabetic(6));\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testGet() {\n    Result<List<Group>> result = weixin.getGroup().get();\n    Assert.assertTrue(result.success());\n    for (Group group : result.getResult()) {\n      logger.info(group.getJsonObject().toString());\n    }\n  }\n\n  @Test\n  public void testUpdate() {\n    String id = System.getProperty(\"weixin.groupid\");\n    Result<Error> result = weixin.getGroup().update(id, RandomStringUtils.randomAlphabetic(6));\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testMove() {\n    String openId = System.getProperty(\"weixin.openid\");\n    String groupId = System.getProperty(\"weixin.groupid\");\n    Result<Error> result = weixin.getGroup().move(openId, groupId);\n    Assert.assertTrue(result.success());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/MediaTest.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.io.File;\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.Media;\nimport com.belerweb.social.weixin.bean.MediaType;\n\npublic class MediaTest extends TestConfig {\n  final static Logger logger = LoggerFactory.getLogger(MediaTest.class);\n\n  @Test\n  public void testUpload() throws Exception {\n    File file = new File(System.getProperty(\"weixin.file\"));\n    Media media = new Media();\n    media.setName(file.getName());\n    media.setContentType(MediaType.VOICE_AMR.contentType());\n    media.setContent(FileUtils.readFileToByteArray(file));\n    Result<com.belerweb.social.weixin.bean.Media> result =\n        weixin.getMedia().upload(MediaType.VOICE_AMR, media);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getId());\n  }\n\n  @Test\n  public void testGet() throws Exception {\n    String mediaId = System.getProperty(\"weixin.mediaid\");\n    Result<Media> result = weixin.getMedia().get(mediaId);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getContentType());\n    logger.info(result.getResult().getName());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/MenuTest.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.RandomStringUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Error;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.Menu;\nimport com.belerweb.social.weixin.bean.MenuType;\n\npublic class MenuTest extends TestConfig {\n\n  @Test\n  public void testGet() {\n    Result<List<Menu>> result = weixin.getMenu().get();\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testDelete() {\n    Result<Error> result = weixin.getMenu().delete();\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testCreate() {\n    List<Menu> menus = new ArrayList<Menu>();\n    Menu menu1 = new Menu();\n    menu1.setName(RandomStringUtils.randomAlphabetic(4));\n    List<Menu> subs = new ArrayList<Menu>();\n    Menu menu11 = new Menu();\n    menu11.setName(RandomStringUtils.randomAlphabetic(4));\n    menu11.setType(MenuType.CLICK);\n    menu11.setKey(RandomStringUtils.randomAlphabetic(4));\n    subs.add(menu11);\n    Menu menu22 = new Menu();\n    menu22.setName(RandomStringUtils.randomAlphabetic(4));\n    menu22.setType(MenuType.VIEW);\n    menu22.setUrl(\"https://github.com/belerweb/\");\n    subs.add(menu22);\n    menu1.setSubs(subs);\n    menus.add(menu1);\n    Result<Error> result = weixin.getMenu().create(menus);\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testCreate1() throws Exception {\n    List<Menu> menus = new ArrayList<Menu>();\n    Menu menu1 = new Menu();\n    menu1.setName(\"普通菜单\");\n    List<Menu> subs1 = new ArrayList<Menu>();\n    Menu menu11 = new Menu();\n    menu11.setName(\"点击事件\");\n    menu11.setType(MenuType.CLICK);\n    menu11.setKey(\"menu-click\");\n    subs1.add(menu11);\n    Menu menu12 = new Menu();\n    menu12.setName(\"跳转URL\");\n    menu12.setType(MenuType.VIEW);\n    menu12.setUrl(\"https://github.com/jdkcn\");\n    subs1.add(menu12);\n    menu1.setSubs(subs1);\n    menus.add(menu1);\n\n    Menu menu2 = new Menu();\n    menu2.setName(\"扫码\");\n    List<Menu> subs2 = new ArrayList<Menu>();\n    Menu menu21 = new Menu();\n    menu21.setName(\"扫码推事件\");\n    menu21.setType(MenuType.SCANCODE_PUSH);\n    menu21.setKey(\"scancode_push\");\n    subs2.add(menu21);\n    Menu menu22 = new Menu();\n    menu22.setName(\"扫码带提示\");\n    menu22.setType(MenuType.SCANCODE_WAITMSG);\n    menu22.setKey(\"scancode_waitmsg\");\n    subs2.add(menu22);\n    Menu menu23 = new Menu();\n    menu23.setName(\"发送位置\");\n    menu23.setType(MenuType.LOCATION_SELECT);\n    menu23.setKey(\"location_select\");\n    subs2.add(menu23);\n    menu2.setSubs(subs2);\n    menus.add(menu2);\n\n    Menu menu3 = new Menu();\n    menu3.setName(\"发图\");\n    List<Menu> subs3 = new ArrayList<Menu>();\n    Menu menu31 = new Menu();\n    menu31.setName(\"系统拍照发图\");\n    menu31.setType(MenuType.PIC_SYSPHOTO);\n    menu31.setKey(\"pic_sysphoto\");\n    subs3.add(menu31);\n    Menu menu32 = new Menu();\n    menu32.setName(\"拍照或者相册发图\");\n    menu32.setType(MenuType.PIC_PHOTO_OR_ALBUM);\n    menu32.setKey(\"pic_photo_or_album\");\n    subs3.add(menu32);\n    Menu menu33 = new Menu();\n    menu33.setName(\"微信相册发图\");\n    menu33.setType(MenuType.PIC_WEIXIN);\n    menu33.setKey(\"pic_weixin\");\n    subs3.add(menu33);\n    menu3.setSubs(subs3);\n    menus.add(menu3);\n    Result<Error> result = weixin.getMenu().create(menus);\n    Assert.assertTrue(result.success());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/OAuth2Test.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.AccessToken;\n\npublic class OAuth2Test extends TestConfig {\n  final static Logger logger = LoggerFactory.getLogger(OAuth2Test.class);\n\n  @Test\n  public void testAuthorize() {\n    String url = weixin.getOAuth2().authorize();\n    // 浏览器不能访问，只能在微信客户端中访问\n    logger.info(url);\n  }\n\n  @Test\n  public void testAccessToken() {\n    Result<AccessToken> tokenResult = weixin.getOAuth2().accessToken(\"code\");\n    Assert.assertTrue(!tokenResult.success());\n    logger.info(tokenResult.getError().toString());\n\n    String code = System.getProperty(\"weixin.code\");\n    tokenResult = weixin.getOAuth2().accessToken(code);\n    Assert.assertTrue(tokenResult.success());\n    logger.info(tokenResult.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testRefreshAccessToken() {\n    String refreshToken = System.getProperty(\"weixin.rtoken\");\n    Result<AccessToken> tokenResult = weixin.getOAuth2().refreshAccessToken(refreshToken);\n    Assert.assertTrue(tokenResult.success());\n    System.out.print(tokenResult.getResult().getJsonObject());\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/UserTest.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport java.util.List;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\n\npublic class UserTest extends TestConfig {\n\n  final static Logger logger = LoggerFactory.getLogger(UserTest.class);\n\n  @Test\n  public void testSnsapiUserInfo() {\n    String accessToken = System.getProperty(\"weixin.atoken\");\n    String openId = System.getProperty(\"weixin.openid\");\n    Result<com.belerweb.social.weixin.bean.User> result =\n        weixin.getUser().snsapiUserInfo(accessToken, openId);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testUserInfo() {\n    String openId = System.getProperty(\"weixin.openid\");\n    Result<com.belerweb.social.weixin.bean.User> result =\n        weixin.getUser().userInfo(weixin.getAccessToken().getToken(), openId);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testGetFollowUsers() {\n    Result<List<com.belerweb.social.weixin.bean.User>> result = weixin.getUser().getFollowUsers();\n    Assert.assertTrue(result.success());\n    for (com.belerweb.social.weixin.bean.User user : result.getResult()) {\n      logger.info(user.getJsonObject().toString());\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/test/java/com/belerweb/social/weixin/api/WeixinTest.java",
    "content": "package com.belerweb.social.weixin.api;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.Date;\n\nimport org.apache.commons.lang.RandomStringUtils;\nimport org.apache.commons.lang.time.DateFormatUtils;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.belerweb.social.TestConfig;\nimport com.belerweb.social.bean.Result;\nimport com.belerweb.social.weixin.bean.AccessToken;\nimport com.belerweb.social.weixin.bean.ApiTicket;\nimport com.belerweb.social.weixin.bean.JSApiTicket;\nimport com.belerweb.social.weixin.bean.Message;\nimport com.belerweb.social.weixin.bean.MsgType;\nimport com.belerweb.social.weixin.bean.QRTicket;\nimport com.belerweb.social.weixin.bean.QRType;\nimport com.belerweb.social.weixin.bean.Variable;\n\npublic class WeixinTest extends TestConfig {\n  final static Logger logger = LoggerFactory.getLogger(WeixinTest.class);\n\n  @Test\n  public void testGetAccessToken() {\n    AccessToken result = weixin.getAccessToken();\n    Assert.assertNotNull(result);\n    logger.info(result.getJsonObject().toString());\n  }\n\n  @Test\n  public void testGetJSApiTicket() {\n    JSApiTicket result = weixin.getJsApiTicket();\n    Assert.assertNotNull(result);\n    logger.info(result.getJsonObject().toString());\n  }\n\n  @Test\n  public void testGetApiTicket() {\n    ApiTicket result = weixin.getApiTicket();\n    Assert.assertNotNull(result);\n    logger.info(result.getJsonObject().toString());\n  }\n\n  @Test\n  public void testSignature() throws Exception {\n    Weixin wx = new Weixin(null);\n    assertEquals(\"def42db04eb64f66c47a4e14fcc736156a704d8e\", wx.signature(\"sduhi123\",\n        \"wxd0f84fbc9396d6ae\", \"GROUPON\",\n        \"E0o2-at6NcC2OsJiQTlwlKQmnidi_i9qnUG6I8wOFOOnPaK_fcjapbiBA15AUBXvkux2vvNsjYomRcbxXolfMw\",\n        \"14300000000\", \"134234235235235\"));\n  }\n\n  @Test\n  public void testJsApiSignature() {\n    String signature =\n        weixin.jsapiSignature(\"http://openlaw.cn\", new Date().getTime(),\n            RandomStringUtils.randomAlphanumeric(16));\n    Assert.assertNotNull(signature);\n    logger.info(signature);\n  }\n\n  @Test\n  public void testCreateQR() {\n    Result<QRTicket> result = weixin.createQR(QRType.QR_SCENE, 0);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n  }\n\n  @Test\n  public void testCreateQR2() {\n    Result<QRTicket> result = weixin.createQR(QRType.QR_LIMIT_SCENE, 0);\n    Assert.assertTrue(result.success());\n    logger.info(result.getResult().getJsonObject().toString());\n    logger.info(result.getResult().getQRUrl());\n  }\n\n  @Test\n  public void testSendCustomMessage() {\n    Message message = new Message(MsgType.TEXT);\n    message.setToUser(System.getProperty(\"weixin.openid\"));\n    message.setContent(new Date().toString());\n    Result<Boolean> result = weixin.sendCustomMessage(message);\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testSendTemplateMessage() throws Exception {\n    Message message = new Message(MsgType.TEMPLATE);\n    message.setToUser(System.getProperty(\"weixin.openid\"));\n    message.setUrl(\"https://github.com/belerweb/social-sdk\");\n    message.setTopColor(\"#459ae9\");\n    message.setTemplateId(System.getProperty(\"weixin.templateid\"));\n    Variable var1 = new Variable(\"first\", \"您好，这是一个模板消息的测试\");\n    Variable var2 = new Variable(\"schedule\", \"这是一个新的事件\");\n    Variable var3 = new Variable(\"time\", DateFormatUtils.format(new Date(), \"yyyy年MM月dd日 HH:mm\"));\n    message.addVariable(var1).addVariable(var2).addVariable(var3);\n    Result<Boolean> result = weixin.sendTemplateMessage(message);\n    Assert.assertTrue(result.success());\n  }\n\n  @Test\n  public void testSendTemplateMessage1() throws Exception {\n    Message message = new Message(MsgType.TEMPLATE);\n    message.setToUser(System.getProperty(\"weixin.openid\"));\n    message.setUrl(\"http://openlaw.cn/judgement/4b47137610264874a4e41f3635b3fd83\");\n    message.setTopColor(\"#459ae9\");\n    message.setTemplateId(System.getProperty(\"weixin.templateid\"));\n    Variable var1 =\n        new Variable(\"first\", \"高圆圆,您好!您关注的判决文书:\\n龚喜琴与上海诺盛企业发展有限公司劳动合同纠纷一审民事判决书\\n有新的回复。\");\n    Variable var2 = new Variable(\"keyword1\", \"happyboy_1688\");\n    Variable var3 =\n        new Variable(\"keyword2\", DateFormatUtils.format(new Date(), \"yyyy年MM月dd日 HH:mm\"));\n    Variable var4 =\n        new Variable(\n            \"keyword3\",\n            \"本人参与庭审,有权发表意见:\\n原告负责的项目她没本事拿下来,本人花了几个月把这项目做下来,老板为显示公平,却把一半提成给了原告,本人只拿一半帮原告挣钱但毫无怨言,这事大家都同意了,现在原告在法庭上反悔要本人的那一半,还好法官有眼,没给她.正义战胜邪恶！ \",\n            \"#32a3cb\");\n    message.addVariable(var1).addVariable(var2).addVariable(var3).addVariable(var4);\n    Result<Boolean> result = weixin.sendTemplateMessage(message);\n    Assert.assertTrue(result.success());\n  }\n}\n"
  },
  {
    "path": "src/test/resources/logback.xml",
    "content": "<configuration>\n  <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n    <!-- encoders are assigned the type\n         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n    <encoder>\n      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n    </encoder>\n  </appender>\n\n  <root level=\"debug\">\n    <appender-ref ref=\"STDOUT\" />\n  </root>\n</configuration>"
  }
]