├── README.md ├── src ├── main │ ├── java │ │ ├── META-INF │ │ │ └── MANIFEST.MF │ │ └── top │ │ │ └── itning │ │ │ ├── webqq │ │ │ ├── model │ │ │ │ ├── Recent.java │ │ │ │ ├── Birthday.java │ │ │ │ ├── Discuss.java │ │ │ │ ├── DiscussInfo.java │ │ │ │ ├── FriendStatus.java │ │ │ │ ├── Group.java │ │ │ │ ├── DiscussUser.java │ │ │ │ ├── Friend.java │ │ │ │ ├── Font.java │ │ │ │ ├── Category.java │ │ │ │ ├── GroupInfo.java │ │ │ │ ├── Message.java │ │ │ │ ├── DiscussMessage.java │ │ │ │ ├── GroupMessage.java │ │ │ │ ├── GroupUser.java │ │ │ │ └── UserInfo.java │ │ │ ├── callback │ │ │ │ └── MessageCallback.java │ │ │ ├── constant │ │ │ │ └── ApiURL.java │ │ │ └── client │ │ │ │ └── SmartQQClient.java │ │ │ ├── util │ │ │ └── PropertiesUtil.java │ │ │ ├── curriculum │ │ │ ├── CurriculumClient.java │ │ │ └── CurriculumEnum.java │ │ │ ├── weather │ │ │ ├── entity │ │ │ │ ├── Weather.java │ │ │ │ ├── WeatherData.java │ │ │ │ └── WeatherInfo.java │ │ │ └── client │ │ │ │ └── WeatherInfoClient.java │ │ │ ├── Application.java │ │ │ └── timer │ │ │ └── TimerTasks.java │ └── resources │ │ └── log4j.properties └── test │ └── java │ └── Test.java ├── configurationInfo.properties ├── .gitignore ├── LICENSE └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # QQrobot 2 | QQ机器人 3 | -------------------------------------------------------------------------------- /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: top.itning.Application 3 | 4 | -------------------------------------------------------------------------------- /configurationInfo.properties: -------------------------------------------------------------------------------- 1 | #管理员QQ昵称 2 | adminName=她姓许º 3 | #需要发送消息的群昵称 4 | groupName=╭ァ編徎縱扖門到瓬棄 5 | #关闭命令 6 | close_command=close 7 | #每次执行时间间隔(ms) 24 * 60 * 60 * 1000 8 | time_period=8640000 9 | #时间 10 | run_time=21:21:0 -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO,stdout 2 | 3 | #Console Appender 4 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 | log4j.appender.stdout.Target=System.out 6 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.stdout.layout.ConversionPattern=%c %x - %m%n -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | .idea/ 27 | Qqrobot.iml 28 | out/artifacts/Qqrobot_jar/qrcode.png 29 | qrcode.png 30 | target/classes/log4j.properties 31 | target/generated-sources/ 32 | target/generated-test-sources/ 33 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Recent.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | /** 4 | * 最近会话. 5 | * 6 | * @author ScienJus 7 | * @author Liang Ding 8 | * @date 2015/12/24. 9 | */ 10 | public class Recent { 11 | 12 | private long uin; 13 | 14 | //0:好友、1:群、2:讨论组 15 | private int type; 16 | 17 | public long getUin() { 18 | return uin; 19 | } 20 | 21 | public void setUin(long uin) { 22 | this.uin = uin; 23 | } 24 | 25 | public int getType() { 26 | return type; 27 | } 28 | 29 | public void setType(int type) { 30 | this.type = type; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/callback/MessageCallback.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.callback; 2 | 3 | 4 | import top.itning.webqq.model.DiscussMessage; 5 | import top.itning.webqq.model.GroupMessage; 6 | import top.itning.webqq.model.Message; 7 | 8 | /** 9 | * 收到消息的回调 10 | * @author ScienJus 11 | * @date 2015/12/18. 12 | */ 13 | public interface MessageCallback { 14 | 15 | /** 16 | * 收到私聊消息后的回调 17 | * @param message 私聊消息 18 | */ 19 | void onMessage(Message message); 20 | 21 | /** 22 | * 收到群消息后的回调 23 | * @param message 群消息 24 | */ 25 | void onGroupMessage(GroupMessage message); 26 | 27 | /** 28 | * 收到讨论组消息后的回调 29 | * @param message 讨论组消息 30 | */ 31 | void onDiscussMessage(DiscussMessage message); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Birthday.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | /** 4 | * 生日. 5 | * 6 | * @author ScienJus 7 | * @author Liang Ding 8 | * @date 2015/12/24. 9 | */ 10 | public class Birthday { 11 | 12 | private int year; 13 | 14 | private int month; 15 | 16 | private int day; 17 | 18 | public int getYear() { 19 | return year; 20 | } 21 | 22 | public void setYear(int year) { 23 | this.year = year; 24 | } 25 | 26 | public int getMonth() { 27 | return month; 28 | } 29 | 30 | public void setMonth(int month) { 31 | this.month = month; 32 | } 33 | 34 | public int getDay() { 35 | return day; 36 | } 37 | 38 | public void setDay(int day) { 39 | this.day = day; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/top/itning/util/PropertiesUtil.java: -------------------------------------------------------------------------------- 1 | package top.itning.util; 2 | 3 | import java.io.*; 4 | import java.util.Properties; 5 | 6 | /*** 7 | * 8 | * @author : ning 9 | * @version : 1.0.0 10 | * @date : 2017/11/7 11 | **/ 12 | public class PropertiesUtil { 13 | private final static Properties PROPERTIES = new Properties(); 14 | 15 | private PropertiesUtil() { 16 | } 17 | 18 | public static String getValueByKey(String key, String url) { 19 | if (key != null) { 20 | try { 21 | //PROPERTIES.load(new FileReader("")); 22 | PROPERTIES.load(new InputStreamReader(new FileInputStream(url),"utf-8")); 23 | } catch (IOException e) { 24 | System.err.println("url error"); 25 | e.printStackTrace(); 26 | } 27 | return PROPERTIES.getProperty(key); 28 | } 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Discuss.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 讨论组. 7 | * 8 | * @author ScienJus 9 | * @author Liang Ding 10 | * @date 2015/12/23. 11 | */ 12 | public class Discuss { 13 | 14 | @JSONField(name = "did") 15 | private long id; 16 | 17 | private String name; 18 | 19 | public long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getName() { 28 | return name; 29 | } 30 | 31 | public void setName(String name) { 32 | this.name = name; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Discuss{" + 38 | "id=" + id + 39 | ", name='" + name + '\'' + 40 | '}'; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 a1837634447 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/DiscussInfo.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 讨论组资料. 10 | * 11 | * @author ScienJus 12 | * @author Liang Ding 13 | * @date 2015/12/24. 14 | */ 15 | public class DiscussInfo { 16 | 17 | @JSONField(name = "did") 18 | private long id; 19 | 20 | @JSONField(name = "discu_name") 21 | private String name; 22 | 23 | private List users = new ArrayList<>(); 24 | 25 | public void addUser(DiscussUser user) { 26 | this.users.add(user); 27 | } 28 | 29 | public long getId() { 30 | return id; 31 | } 32 | 33 | public void setId(long id) { 34 | this.id = id; 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | 45 | public List getUsers() { 46 | return users; 47 | } 48 | 49 | public void setUsers(List users) { 50 | this.users = users; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/FriendStatus.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 好友状态. 7 | * 8 | * @author ScienJus 9 | * @author Liang Ding 10 | * @date 2015/12/24. 11 | */ 12 | public class FriendStatus { 13 | 14 | private long uin; 15 | 16 | private String status; 17 | 18 | @JSONField(name = "client_type") 19 | private int clientType; 20 | 21 | public long getUin() { 22 | return uin; 23 | } 24 | 25 | public void setUin(long uin) { 26 | this.uin = uin; 27 | } 28 | 29 | public String getStatus() { 30 | return status; 31 | } 32 | 33 | public void setStatus(String status) { 34 | this.status = status; 35 | } 36 | 37 | public int getClientType() { 38 | return clientType; 39 | } 40 | 41 | public void setClientType(int clientType) { 42 | this.clientType = clientType; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "FriendStatus{" + 48 | "uin=" + uin + 49 | ", status='" + status + '\'' + 50 | ", clientType=" + clientType + 51 | '}'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/top/itning/curriculum/CurriculumClient.java: -------------------------------------------------------------------------------- 1 | package top.itning.curriculum; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Locale; 6 | 7 | /** 8 | * @author wangn 9 | */ 10 | public class CurriculumClient { 11 | private CurriculumClient() { 12 | } 13 | 14 | public static String getClassInfo() { 15 | String day = new SimpleDateFormat("EE", Locale.US).format(new Date()); 16 | String info = ""; 17 | switch (day) { 18 | case "Mon": 19 | info = CurriculumEnum.Monday.getClassInfo(); 20 | break; 21 | case "Tue": 22 | info = CurriculumEnum.Tuesday.getClassInfo(); 23 | break; 24 | case "Wed": 25 | info = CurriculumEnum.Wednesday.getClassInfo(); 26 | break; 27 | case "Thu": 28 | info = CurriculumEnum.Thursday.getClassInfo(); 29 | break; 30 | case "Fri": 31 | info = CurriculumEnum.Friday.getClassInfo(); 32 | break; 33 | default: 34 | } 35 | return ((!"星期六".equals(day)) ? "今天课程:\n" : "") + info + (("星期五".equals(day) || "星期六".equals(day)) ? "" : "晚自习:A301"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Group.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 群. 7 | * 8 | * @author ScienJus 9 | * @author Liang Ding 10 | * @date 2015/12/18. 11 | */ 12 | public class Group { 13 | 14 | @JSONField(name = "gid") 15 | private long id; 16 | 17 | private String name; 18 | 19 | private long flag; 20 | 21 | private long code; 22 | 23 | public long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(long id) { 28 | this.id = id; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public long getFlag() { 40 | return flag; 41 | } 42 | 43 | public void setFlag(long flag) { 44 | this.flag = flag; 45 | } 46 | 47 | public long getCode() { 48 | return code; 49 | } 50 | 51 | public void setCode(long code) { 52 | this.code = code; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Group{" + 58 | "id=" + id + 59 | ", name='" + name + '\'' + 60 | ", flag=" + flag + 61 | ", code=" + code + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/DiscussUser.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | /** 4 | * 讨论组成员. 5 | * 6 | * @author ScienJus 7 | * @author Liang Ding 8 | * @date 2015/12/24. 9 | */ 10 | public class DiscussUser { 11 | 12 | private long uin; 13 | 14 | private String nick; 15 | 16 | private int clientType; 17 | 18 | private String status; 19 | 20 | @Override 21 | public String toString() { 22 | return "DiscussUser{" 23 | + "uin=" + uin 24 | + ", nick='" + nick + '\'' 25 | + ", clientType='" + clientType + '\'' 26 | + ", status='" + status + '\'' 27 | + '}'; 28 | } 29 | 30 | public long getUin() { 31 | return uin; 32 | } 33 | 34 | public void setUin(long uin) { 35 | this.uin = uin; 36 | } 37 | 38 | public String getNick() { 39 | return nick; 40 | } 41 | 42 | public void setNick(String nick) { 43 | this.nick = nick; 44 | } 45 | 46 | public int getClientType() { 47 | return clientType; 48 | } 49 | 50 | public void setClientType(int clientType) { 51 | this.clientType = clientType; 52 | } 53 | 54 | public String getStatus() { 55 | return status; 56 | } 57 | 58 | public void setStatus(String status) { 59 | this.status = status; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/top/itning/curriculum/CurriculumEnum.java: -------------------------------------------------------------------------------- 1 | package top.itning.curriculum; 2 | 3 | /** 4 | * @author wangn 5 | */ 6 | public enum CurriculumEnum { 7 | Monday( 8 | "", 9 | "三四节:篮球 外聘2 地点:篮球场", 10 | "五六节:互联网程序设计 梁海红 地点:B313", 11 | "七八节:英语四六级 李鑫 地点:A301" 12 | ), 13 | Tuesday( 14 | "一二节:英语四六级 李鑫 地点:A301", 15 | "三四节:线性代数 徐延新 地点:A330", 16 | "", 17 | "" 18 | ), 19 | Wednesday( 20 | "一二节:互联网程序设计 梁海红 地点:B313", 21 | "三四节:JAVA程序设计 于洪 地点:B202", 22 | "五六节:模电数电 华晓杰 地点:B313", 23 | "" 24 | ), 25 | Thursday( 26 | "一二节:计算机组成原理 夏庆英 地点:B312", 27 | "三四节:线性代数 徐延新 地点:A401", 28 | "五六节:JAVA程序设计 于洪 地点:B317", 29 | "五六节:计算机组成原理 夏庆英 地点:B211" 30 | ), 31 | Friday( 32 | "一二节:模电数电 华晓杰 地点:A221", 33 | "", 34 | "", 35 | "" 36 | ); 37 | 38 | private String class1; 39 | private String class2; 40 | private String class3; 41 | private String class4; 42 | 43 | CurriculumEnum(String class1, String class2, String class3, String class4) { 44 | this.class1 = class1; 45 | this.class2 = class2; 46 | this.class3 = class3; 47 | this.class4 = class4; 48 | } 49 | 50 | public String getClassInfo() { 51 | return class1 + "\n" + class2 + "\n" + class3 + "\n" + class4 + "\n"; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Friend.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | /** 4 | * 好友. 5 | * 6 | * @author ScienJus 7 | * @author Liang Ding 8 | * @date 2015/12/18. 9 | */ 10 | public class Friend { 11 | 12 | private long userId; 13 | 14 | private String markname = ""; 15 | 16 | private String nickname; 17 | 18 | private boolean vip; 19 | 20 | private int vipLevel; 21 | 22 | @Override 23 | public String toString() { 24 | return "Friend{" 25 | + "userId=" + userId 26 | + ", markname='" + markname + '\'' 27 | + ", nickname='" + nickname + '\'' 28 | + ", vip=" + vip 29 | + ", vipLevel=" + vipLevel 30 | + '}'; 31 | } 32 | 33 | public long getUserId() { 34 | return userId; 35 | } 36 | 37 | public void setUserId(long userId) { 38 | this.userId = userId; 39 | } 40 | 41 | public String getMarkname() { 42 | return markname; 43 | } 44 | 45 | public void setMarkname(String markname) { 46 | this.markname = markname; 47 | } 48 | 49 | public String getNickname() { 50 | return nickname; 51 | } 52 | 53 | public void setNickname(String nickname) { 54 | this.nickname = nickname; 55 | } 56 | 57 | public boolean isVip() { 58 | return vip; 59 | } 60 | 61 | public void setVip(boolean vip) { 62 | this.vip = vip; 63 | } 64 | 65 | public int getVipLevel() { 66 | return vipLevel; 67 | } 68 | 69 | public void setVipLevel(int vipLevel) { 70 | this.vipLevel = vipLevel; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Font.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * 字体. 7 | * 8 | * @author ScienJus 9 | * @author Liang Ding 10 | * @date 15/12/19. 11 | */ 12 | public class Font { 13 | 14 | public static final Font DEFAULT_FONT = defaultFont(); 15 | 16 | private static Font defaultFont() { 17 | Font font = new Font(); 18 | font.setColor("000000"); 19 | font.setStyle(new int[]{0, 0, 0}); 20 | font.setName("宋体"); 21 | font.setSize(10); 22 | return font; 23 | } 24 | 25 | private int[] style; 26 | 27 | private String color; 28 | 29 | private String name; 30 | 31 | private int size; 32 | 33 | public int[] getStyle() { 34 | return style; 35 | } 36 | 37 | public void setStyle(int[] style) { 38 | this.style = style; 39 | } 40 | 41 | public String getColor() { 42 | return color; 43 | } 44 | 45 | public void setColor(String color) { 46 | this.color = color; 47 | } 48 | 49 | public String getName() { 50 | return name; 51 | } 52 | 53 | public void setName(String name) { 54 | this.name = name; 55 | } 56 | 57 | public int getSize() { 58 | return size; 59 | } 60 | 61 | public void setSize(int size) { 62 | this.size = size; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "Font{" + 68 | "style=" + Arrays.toString(style) + 69 | ", color='" + color + '\'' + 70 | ", name='" + name + '\'' + 71 | ", size=" + size + 72 | '}'; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Category.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 分组. 8 | * 9 | * @author ScienJus 10 | * @author Liang Ding 11 | * @date 15/12/19. 12 | */ 13 | public class Category { 14 | 15 | private int index; 16 | 17 | private int sort; 18 | 19 | private String name; 20 | 21 | private List friends = new ArrayList<>(); 22 | 23 | public void addFriend(Friend friend) { 24 | this.friends.add(friend); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "Category{" 30 | + "index=" + index 31 | + ", sort=" + sort 32 | + ", name='" + name + '\'' 33 | + ", friends=" + friends 34 | + '}'; 35 | } 36 | 37 | public static Category defaultCategory() { 38 | Category category = new Category(); 39 | category.setIndex(0); 40 | category.setSort(0); 41 | category.setName("我的好友"); 42 | return category; 43 | } 44 | 45 | public int getIndex() { 46 | return index; 47 | } 48 | 49 | public void setIndex(int index) { 50 | this.index = index; 51 | } 52 | 53 | public int getSort() { 54 | return sort; 55 | } 56 | 57 | public void setSort(int sort) { 58 | this.sort = sort; 59 | } 60 | 61 | public String getName() { 62 | return name; 63 | } 64 | 65 | public void setName(String name) { 66 | this.name = name; 67 | } 68 | 69 | public List getFriends() { 70 | return friends; 71 | } 72 | 73 | public void setFriends(List friends) { 74 | this.friends = friends; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/top/itning/weather/entity/Weather.java: -------------------------------------------------------------------------------- 1 | package top.itning.weather.entity; 2 | 3 | /*** 4 | * 5 | * @author : ning 6 | * @version : 1.0.0 7 | * @date : 2017/11/7 8 | **/ 9 | public class Weather { 10 | private String date; 11 | private String message; 12 | private Integer status; 13 | private String city; 14 | private Integer count; 15 | private WeatherData data; 16 | 17 | public String getDate() { 18 | return date; 19 | } 20 | 21 | public void setDate(String date) { 22 | this.date = date; 23 | } 24 | 25 | public String getMessage() { 26 | return message; 27 | } 28 | 29 | public void setMessage(String message) { 30 | this.message = message; 31 | } 32 | 33 | public Integer getStatus() { 34 | return status; 35 | } 36 | 37 | public void setStatus(Integer status) { 38 | this.status = status; 39 | } 40 | 41 | public String getCity() { 42 | return city; 43 | } 44 | 45 | public void setCity(String city) { 46 | this.city = city; 47 | } 48 | 49 | public Integer getCount() { 50 | return count; 51 | } 52 | 53 | public void setCount(Integer count) { 54 | this.count = count; 55 | } 56 | 57 | public WeatherData getData() { 58 | return data; 59 | } 60 | 61 | public void setData(WeatherData data) { 62 | this.data = data; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "Weather{" + 68 | "date='" + date + '\'' + 69 | ", message='" + message + '\'' + 70 | ", status=" + status + 71 | ", city='" + city + '\'' + 72 | ", count=" + count + 73 | ", data=" + data + 74 | '}'; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/GroupInfo.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 群资料. 8 | * 9 | * @author ScienJus 10 | * @author Liang Ding 11 | * @date 2015/12/24. 12 | */ 13 | public class GroupInfo { 14 | 15 | private long gid; 16 | 17 | private long createtime; 18 | 19 | private String memo; 20 | 21 | private String name; 22 | 23 | private long owner; 24 | 25 | private String markname; 26 | 27 | private List users = new ArrayList<>(); 28 | 29 | public void addUser(GroupUser user) { 30 | this.users.add(user); 31 | } 32 | 33 | public long getGid() { 34 | return gid; 35 | } 36 | 37 | public void setGid(long gid) { 38 | this.gid = gid; 39 | } 40 | 41 | public long getCreatetime() { 42 | return createtime; 43 | } 44 | 45 | public void setCreatetime(long createtime) { 46 | this.createtime = createtime; 47 | } 48 | 49 | public String getMemo() { 50 | return memo; 51 | } 52 | 53 | public void setMemo(String memo) { 54 | this.memo = memo; 55 | } 56 | 57 | public String getName() { 58 | return name; 59 | } 60 | 61 | public void setName(String name) { 62 | this.name = name; 63 | } 64 | 65 | public long getOwner() { 66 | return owner; 67 | } 68 | 69 | public void setOwner(long owner) { 70 | this.owner = owner; 71 | } 72 | 73 | public String getMarkname() { 74 | return markname; 75 | } 76 | 77 | public void setMarkname(String markname) { 78 | this.markname = markname; 79 | } 80 | 81 | public List getUsers() { 82 | return users; 83 | } 84 | 85 | public void setUsers(List users) { 86 | this.users = users; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/Message.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | /** 7 | * 消息. 8 | * 9 | * @author ScienJus 10 | * @author Liang Ding 11 | * @date 15/12/19. 12 | */ 13 | public class Message { 14 | 15 | private long time; 16 | 17 | private String content; 18 | 19 | private long userId; 20 | 21 | private Font font; 22 | 23 | public Message(JSONObject json) { 24 | JSONArray cont = json.getJSONArray("content"); 25 | this.font = cont.getJSONArray(0).getObject(1, Font.class); 26 | 27 | final int size = cont.size(); 28 | final StringBuilder contentBuilder = new StringBuilder(); 29 | for (int i = 1; i < size; i++) { 30 | contentBuilder.append(cont.getString(i)); 31 | } 32 | this.content = contentBuilder.toString(); 33 | 34 | this.time = json.getLongValue("time"); 35 | this.userId = json.getLongValue("from_uin"); 36 | } 37 | 38 | public long getTime() { 39 | return time; 40 | } 41 | 42 | public void setTime(long time) { 43 | this.time = time; 44 | } 45 | 46 | public String getContent() { 47 | return content; 48 | } 49 | 50 | public void setContent(String content) { 51 | this.content = content; 52 | } 53 | 54 | public long getUserId() { 55 | return userId; 56 | } 57 | 58 | public void setUserId(long userId) { 59 | this.userId = userId; 60 | } 61 | 62 | public Font getFont() { 63 | return font; 64 | } 65 | 66 | public void setFont(Font font) { 67 | this.font = font; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "Message{" + 73 | "time=" + time + 74 | ", content='" + content + '\'' + 75 | ", userId=" + userId + 76 | ", font=" + font + 77 | '}'; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | top.itning 8 | qqrobot 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 1.6 17 | 1.6 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.12 29 | test 30 | 31 | 32 | 33 | log4j 34 | log4j 35 | 1.2.17 36 | 37 | 38 | 39 | com.alibaba 40 | fastjson 41 | 1.2.7 42 | 43 | 44 | 45 | net.dongliu 46 | requests 47 | 2.1.5 48 | 49 | 50 | 51 | 52 | com.google.guava 53 | guava 54 | 23.3-jre 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/top/itning/weather/entity/WeatherData.java: -------------------------------------------------------------------------------- 1 | package top.itning.weather.entity; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author wangn 7 | */ 8 | public class WeatherData { 9 | private String shidu; 10 | private Integer pm25; 11 | private Integer pm10; 12 | private String quality; 13 | private String wendu; 14 | private String ganmao; 15 | private WeatherInfo[] forecast; 16 | 17 | public String getShidu() { 18 | return shidu; 19 | } 20 | 21 | public void setShidu(String shidu) { 22 | this.shidu = shidu; 23 | } 24 | 25 | public Integer getPm25() { 26 | return pm25; 27 | } 28 | 29 | public void setPm25(Integer pm25) { 30 | this.pm25 = pm25; 31 | } 32 | 33 | public Integer getPm10() { 34 | return pm10; 35 | } 36 | 37 | public void setPm10(Integer pm10) { 38 | this.pm10 = pm10; 39 | } 40 | 41 | public String getQuality() { 42 | return quality; 43 | } 44 | 45 | public void setQuality(String quality) { 46 | this.quality = quality; 47 | } 48 | 49 | public String getWendu() { 50 | return wendu; 51 | } 52 | 53 | public void setWendu(String wendu) { 54 | this.wendu = wendu; 55 | } 56 | 57 | public String getGanmao() { 58 | return ganmao; 59 | } 60 | 61 | public void setGanmao(String ganmao) { 62 | this.ganmao = ganmao; 63 | } 64 | 65 | public WeatherInfo[] getForecast() { 66 | return forecast; 67 | } 68 | 69 | public void setForecast(WeatherInfo[] forecast) { 70 | this.forecast = forecast; 71 | } 72 | 73 | @Override 74 | public String toString() { 75 | return "WeatherData{" + 76 | "shidu='" + shidu + '\'' + 77 | ", pm25=" + pm25 + 78 | ", pm10=" + pm10 + 79 | ", quality='" + quality + '\'' + 80 | ", wendu='" + wendu + '\'' + 81 | ", ganmao='" + ganmao + '\'' + 82 | ", forecast=" + Arrays.toString(forecast) + 83 | '}'; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/DiscussMessage.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | /** 7 | * 讨论组消息. 8 | * 9 | * @author ScienJus 10 | * @author Liang Ding 11 | * @date 15/12/19. 12 | */ 13 | public class DiscussMessage { 14 | 15 | private long discussId; 16 | 17 | private long time; 18 | 19 | private String content; 20 | 21 | private long userId; 22 | 23 | private Font font; 24 | 25 | public DiscussMessage(JSONObject json) { 26 | JSONArray content = json.getJSONArray("content"); 27 | this.font = content.getJSONArray(0).getObject(1, Font.class); 28 | this.content = content.getString(1); 29 | if (content.size() > 2) 30 | this.content += content.getString(3); 31 | this.time = json.getLongValue("time"); 32 | this.discussId = json.getLongValue("did"); 33 | this.userId = json.getLongValue("send_uin"); 34 | } 35 | 36 | public long getDiscussId() { 37 | return discussId; 38 | } 39 | 40 | public void setDiscussId(long discussId) { 41 | this.discussId = discussId; 42 | } 43 | 44 | public long getTime() { 45 | return time; 46 | } 47 | 48 | public void setTime(long time) { 49 | this.time = time; 50 | } 51 | 52 | public String getContent() { 53 | return content; 54 | } 55 | 56 | public void setContent(String content) { 57 | this.content = content; 58 | } 59 | 60 | public long getUserId() { 61 | return userId; 62 | } 63 | 64 | public void setUserId(long userId) { 65 | this.userId = userId; 66 | } 67 | 68 | public Font getFont() { 69 | return font; 70 | } 71 | 72 | public void setFont(Font font) { 73 | this.font = font; 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return "DiscussMessage{" + 79 | "discussId=" + discussId + 80 | ", time=" + time + 81 | ", content='" + content + '\'' + 82 | ", userId=" + userId + 83 | ", font=" + font + 84 | '}'; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/GroupMessage.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.JSONArray; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | /** 7 | * 群消息. 8 | * 9 | * @author ScienJus 10 | * @author Liang Ding 11 | * @date 15/12/19. 12 | */ 13 | public class GroupMessage { 14 | 15 | private long groupId; 16 | 17 | private long time; 18 | 19 | private String content; 20 | 21 | private long userId; 22 | 23 | private Font font; 24 | 25 | public GroupMessage(JSONObject json) { 26 | JSONArray cont = json.getJSONArray("content"); 27 | this.font = cont.getJSONArray(0).getObject(1, Font.class); 28 | 29 | final int size = cont.size(); 30 | final StringBuilder contentBuilder = new StringBuilder(); 31 | for (int i = 1; i < size; i++) { 32 | contentBuilder.append(cont.getString(i)); 33 | } 34 | this.content = contentBuilder.toString(); 35 | 36 | this.time = json.getLongValue("time"); 37 | this.groupId = json.getLongValue("group_code"); 38 | this.userId = json.getLongValue("send_uin"); 39 | } 40 | 41 | public long getGroupId() { 42 | return groupId; 43 | } 44 | 45 | public void setGroupId(long groupId) { 46 | this.groupId = groupId; 47 | } 48 | 49 | public long getTime() { 50 | return time; 51 | } 52 | 53 | public void setTime(long time) { 54 | this.time = time; 55 | } 56 | 57 | public String getContent() { 58 | return content; 59 | } 60 | 61 | public void setContent(String content) { 62 | this.content = content; 63 | } 64 | 65 | public long getUserId() { 66 | return userId; 67 | } 68 | 69 | public void setUserId(long userId) { 70 | this.userId = userId; 71 | } 72 | 73 | public Font getFont() { 74 | return font; 75 | } 76 | 77 | public void setFont(Font font) { 78 | this.font = font; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "GroupMessage{" + 84 | "groupId=" + groupId + 85 | ", time=" + time + 86 | ", content='" + content + '\'' + 87 | ", userId=" + userId + 88 | ", font=" + font + 89 | '}'; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/top/itning/weather/client/WeatherInfoClient.java: -------------------------------------------------------------------------------- 1 | package top.itning.weather.client; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import net.dongliu.requests.Client; 5 | import net.dongliu.requests.Session; 6 | import top.itning.weather.entity.Weather; 7 | import top.itning.weather.entity.WeatherData; 8 | import top.itning.weather.entity.WeatherInfo; 9 | 10 | import java.text.SimpleDateFormat; 11 | import java.util.Date; 12 | import java.util.Locale; 13 | 14 | /*** 15 | *天气信息客户端 16 | * @author : ning 17 | * @version : 1.0.0 18 | * @date : 2017/11/7 19 | **/ 20 | public class WeatherInfoClient { 21 | private WeatherInfoClient() { 22 | } 23 | 24 | private static final int SUCCESS_CODE = 200; 25 | private static final String URL = "http://www.sojson.com/open/api/weather/json.shtml?city=宾县"; 26 | 27 | /*** 28 | * 获取天气信息 29 | * @author : ning 30 | * @return Weather Bean--top.itning.weather.entity.Weather 31 | * @date : 2017/11/7 32 | **/ 33 | private static Weather getWeatherInfo() { 34 | Client build = Client.pooled().maxPerRoute(5).maxTotal(10).build(); 35 | Session session = build.session(); 36 | String body = session.get(URL).text().getBody(); 37 | JSONObject jsonObject = JSONObject.parseObject(body); 38 | return JSONObject.toJavaObject(jsonObject, Weather.class); 39 | } 40 | 41 | /*** 42 | * 获取格式化后的天气信息 43 | * @author : ning 44 | * @return 格式化后的天气信息--java.lang.String 45 | * @date : 2017/11/7 46 | **/ 47 | public static String getFormatWeatherInfo() { 48 | Weather weatherInfo = getWeatherInfo(); 49 | if (SUCCESS_CODE == weatherInfo.getStatus()) { 50 | WeatherData data = weatherInfo.getData(); 51 | WeatherInfo info = data.getForecast()[0]; 52 | return "今天是" + 53 | new SimpleDateFormat("yyyy年MM月dd日 EE ", Locale.CHINESE).format(new Date()) + 54 | "\n" + weatherInfo.getCity() + "天气:\n湿度:" + data.getShidu() + 55 | "\nPM2.5:" + data.getPm25() + "\n可吸入颗粒物:" + data.getPm10() + "\n" + 56 | data.getQuality() + " " + data.getGanmao() + 57 | "\n日出:" + info.getSunrise() + "\t日落:" + info.getSunset() + "\n" + info.getLow() + " " + info.getHigh() + "\n" + 58 | info.getType() + " " + info.getFx() + " " + info.getFl(); 59 | } 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/top/itning/weather/entity/WeatherInfo.java: -------------------------------------------------------------------------------- 1 | package top.itning.weather.entity; 2 | 3 | /** 4 | * @author wangn 5 | */ 6 | public class WeatherInfo { 7 | private String date; 8 | private String sunrise; 9 | private String high; 10 | private String low; 11 | private String sunset; 12 | private String aqi; 13 | private String fx; 14 | private String fl; 15 | private String type; 16 | 17 | public String getDate() { 18 | return date; 19 | } 20 | 21 | public void setDate(String date) { 22 | this.date = date; 23 | } 24 | 25 | public String getSunrise() { 26 | return sunrise; 27 | } 28 | 29 | public void setSunrise(String sunrise) { 30 | this.sunrise = sunrise; 31 | } 32 | 33 | public String getHigh() { 34 | return high; 35 | } 36 | 37 | public void setHigh(String high) { 38 | this.high = high; 39 | } 40 | 41 | public String getLow() { 42 | return low; 43 | } 44 | 45 | public void setLow(String low) { 46 | this.low = low; 47 | } 48 | 49 | public String getSunset() { 50 | return sunset; 51 | } 52 | 53 | public void setSunset(String sunset) { 54 | this.sunset = sunset; 55 | } 56 | 57 | public String getAqi() { 58 | return aqi; 59 | } 60 | 61 | public void setAqi(String aqi) { 62 | this.aqi = aqi; 63 | } 64 | 65 | public String getFx() { 66 | return fx; 67 | } 68 | 69 | public void setFx(String fx) { 70 | this.fx = fx; 71 | } 72 | 73 | public String getFl() { 74 | return fl; 75 | } 76 | 77 | public void setFl(String fl) { 78 | this.fl = fl; 79 | } 80 | 81 | public String getType() { 82 | return type; 83 | } 84 | 85 | public void setType(String type) { 86 | this.type = type; 87 | } 88 | 89 | @Override 90 | public String toString() { 91 | return "WeatherInfo{" + 92 | "date='" + date + '\'' + 93 | ", sunrise='" + sunrise + '\'' + 94 | ", high='" + high + '\'' + 95 | ", low='" + low + '\'' + 96 | ", sunset='" + sunset + '\'' + 97 | ", aqi='" + aqi + '\'' + 98 | ", fx='" + fx + '\'' + 99 | ", fl='" + fl + '\'' + 100 | ", type='" + type + '\'' + 101 | '}'; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/GroupUser.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | /** 4 | * 群成员. 5 | * 6 | * @author ScienJus 7 | * @author Liang Ding 8 | * @date 2015/12/24. 9 | */ 10 | public class GroupUser { 11 | 12 | private String nick; 13 | 14 | private String province; 15 | 16 | private String gender; 17 | 18 | private long uin; 19 | 20 | private String country; 21 | 22 | private String city; 23 | 24 | private String card; 25 | 26 | private int clientType; 27 | 28 | private int status; 29 | 30 | private boolean vip; 31 | 32 | private int vipLevel; 33 | 34 | public String getNick() { 35 | return nick; 36 | } 37 | 38 | public void setNick(String nick) { 39 | this.nick = nick; 40 | } 41 | 42 | public String getProvince() { 43 | return province; 44 | } 45 | 46 | public void setProvince(String province) { 47 | this.province = province; 48 | } 49 | 50 | public String getGender() { 51 | return gender; 52 | } 53 | 54 | public void setGender(String gender) { 55 | this.gender = gender; 56 | } 57 | 58 | public long getUin() { 59 | return uin; 60 | } 61 | 62 | public void setUin(long uin) { 63 | this.uin = uin; 64 | } 65 | 66 | public String getCountry() { 67 | return country; 68 | } 69 | 70 | public void setCountry(String country) { 71 | this.country = country; 72 | } 73 | 74 | public String getCity() { 75 | return city; 76 | } 77 | 78 | public void setCity(String city) { 79 | this.city = city; 80 | } 81 | 82 | public String getCard() { 83 | return card; 84 | } 85 | 86 | public void setCard(String card) { 87 | this.card = card; 88 | } 89 | 90 | public int getClientType() { 91 | return clientType; 92 | } 93 | 94 | public void setClientType(int clientType) { 95 | this.clientType = clientType; 96 | } 97 | 98 | public int getStatus() { 99 | return status; 100 | } 101 | 102 | public void setStatus(int status) { 103 | this.status = status; 104 | } 105 | 106 | public boolean isVip() { 107 | return vip; 108 | } 109 | 110 | public void setVip(boolean vip) { 111 | this.vip = vip; 112 | } 113 | 114 | public int getVipLevel() { 115 | return vipLevel; 116 | } 117 | 118 | public void setVipLevel(int vipLevel) { 119 | this.vipLevel = vipLevel; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/top/itning/Application.java: -------------------------------------------------------------------------------- 1 | package top.itning; 2 | 3 | 4 | import org.apache.log4j.Logger; 5 | import top.itning.timer.TimerTasks; 6 | import top.itning.util.PropertiesUtil; 7 | import top.itning.webqq.callback.MessageCallback; 8 | import top.itning.webqq.client.SmartQQClient; 9 | import top.itning.webqq.model.*; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | 15 | /** 16 | * 主入口 17 | * 18 | * @author wangn 19 | */ 20 | public class Application { 21 | 22 | private static final Logger LOGGER = Logger.getLogger(Application.class); 23 | 24 | private static long User_ID = 0; 25 | 26 | /** 27 | * 程序关闭命令 28 | */ 29 | private static final String CLOSE_COMMAND = PropertiesUtil.getValueByKey("close_command", "configurationInfo.properties"); 30 | 31 | /** 32 | * 创建一个新对象时需要扫描二维码登录,并且传一个处理接收到消息的回调,如果你不需要接收消息,可以传null 33 | */ 34 | private static SmartQQClient client = new SmartQQClient(new MessageCallback() { 35 | @Override 36 | public void onMessage(Message message) { 37 | if (User_ID == message.getUserId()) { 38 | if (CLOSE_COMMAND.equals(message.getContent())) { 39 | client.sendMessageToFriend(User_ID, "关闭成功"); 40 | close(); 41 | } else { 42 | client.sendMessageToFriend(User_ID, message.getContent()); 43 | } 44 | 45 | } 46 | } 47 | 48 | @Override 49 | public void onGroupMessage(GroupMessage message) { 50 | 51 | } 52 | 53 | @Override 54 | public void onDiscussMessage(DiscussMessage message) { 55 | 56 | } 57 | }); 58 | 59 | public static void main(String[] args) throws IOException, InterruptedException { 60 | String adminName = PropertiesUtil.getValueByKey("adminName", "configurationInfo.properties"); 61 | List friendListWithCategory = client.getFriendListWithCategory(); 62 | for (Category c : friendListWithCategory) { 63 | List friends = c.getFriends(); 64 | for (Friend friend : friends) { 65 | if (adminName.equals(friend.getNickname()) && friend.getUserId() != 1837634447) { 66 | User_ID = friend.getUserId(); 67 | } 68 | } 69 | } 70 | if (User_ID == 0) { 71 | throw new RuntimeException("user id = 0"); 72 | } 73 | LOGGER.debug("已获取到User_ID-->" + User_ID); 74 | client.sendMessageToFriend(User_ID, "已获取到User_ID-->" + User_ID); 75 | client.sendMessageToFriend(User_ID, "正在开启..."); 76 | new TimerTasks(client); 77 | client.sendMessageToFriend(User_ID, "开启成功!"); 78 | } 79 | 80 | /*** 81 | * 程序关闭 82 | * @author : ning 83 | * @date : 2017/11/7 84 | **/ 85 | private static void close() { 86 | try { 87 | client.close(); 88 | System.exit(1); 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/top/itning/timer/TimerTasks.java: -------------------------------------------------------------------------------- 1 | package top.itning.timer; 2 | 3 | import net.dongliu.requests.exception.RequestException; 4 | import org.apache.log4j.Logger; 5 | import top.itning.curriculum.CurriculumClient; 6 | import top.itning.util.PropertiesUtil; 7 | import top.itning.weather.client.WeatherInfoClient; 8 | import top.itning.webqq.client.SmartQQClient; 9 | import top.itning.webqq.model.Group; 10 | 11 | import java.util.*; 12 | 13 | /*** 14 | * 定时器任务类 15 | * @author : ning 16 | * @version : 1.0.0 17 | * @date : 2017/11/7 18 | **/ 19 | public class TimerTasks { 20 | private static final Logger LOGGER = Logger.getLogger(TimerTasks.class); 21 | /** 22 | * 程序应输入的参数 数 23 | */ 24 | private static final int ARGS_LENGTH = 3; 25 | 26 | private SmartQQClient client; 27 | /** 28 | * 重复周期;单位ms 29 | * 24 * 60 * 60 * 1000 30 | */ 31 | private static final long PERIOD_DAY = Long.parseLong(PropertiesUtil.getValueByKey("time_period", "configurationInfo.properties") == null ? "8640000" : PropertiesUtil.getValueByKey("time_period", "configurationInfo.properties")); 32 | 33 | /*** 34 | * 任务线程 35 | * @author : ning 36 | * @version : 1.0.0 37 | * @date : 2017/11/7 38 | **/ 39 | class Task extends TimerTask { 40 | 41 | @Override 42 | public void run() { 43 | // 每次登陆后ID会变 所以动态获取ID 44 | long groupId = 0; 45 | List groupList = client.getGroupList(); 46 | for (Group group : groupList) { 47 | if (PropertiesUtil.getValueByKey("groupName", "configurationInfo.properties").equals(group.getName())) { 48 | groupId = group.getId(); 49 | } 50 | } 51 | LOGGER.debug("已获取群ID-->" + groupId); 52 | if (groupId == 0) { 53 | throw new RuntimeException("group id = 0"); 54 | } 55 | String msg; 56 | if ((msg = WeatherInfoClient.getFormatWeatherInfo()) != null) { 57 | try { 58 | client.sendMessageToGroup(groupId, msg); 59 | } catch (RequestException e) { 60 | System.out.println("java.net.SocketException: Socket Closed"); 61 | } 62 | 63 | } 64 | String classInfo = CurriculumClient.getClassInfo(); 65 | if (!"".equals(classInfo)) { 66 | try { 67 | client.sendMessageToGroup(groupId, classInfo); 68 | } catch (RequestException e) { 69 | System.out.println("java.net.SocketException: Socket Closed"); 70 | } 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * 构造方法 77 | * 78 | * @param client SmartQQClient实例 79 | * @author : ning 80 | **/ 81 | public TimerTasks(SmartQQClient client) { 82 | this.client = client; 83 | String[] runTimes = PropertiesUtil.getValueByKey("run_time", "configurationInfo.properties").split(":"); 84 | if (runTimes.length != ARGS_LENGTH) { 85 | LOGGER.error("runTimes.length != 3-->" + runTimes.length); 86 | System.exit(0); 87 | } 88 | Calendar calendar = Calendar.getInstance(); 89 | calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(runTimes[0])); 90 | calendar.set(Calendar.MINUTE, Integer.parseInt(runTimes[1])); 91 | calendar.set(Calendar.SECOND, Integer.parseInt(runTimes[2])); 92 | //第一次执行定时任务的时间 93 | Date date = calendar.getTime(); 94 | //如果第一次执行定时任务的时间 小于当前的时间 95 | //此时要在 第一次执行定时任务的时间加一天,以便此任务在下个时间点执行。如果不加一天,任务会立即执行。 96 | if (date.before(new Date())) { 97 | date = this.addDay(date); 98 | } 99 | Timer timer = new Timer(); 100 | Task task = new Task(); 101 | //安排指定的任务在指定的时间开始进行重复的固定延迟执行。 102 | timer.schedule(task, date, PERIOD_DAY); 103 | } 104 | 105 | /*** 106 | * 增加日期 107 | * @author : ning 108 | * @param date Date实例 109 | * @return Date实例--java.util.Date 110 | * @date : 2017/11/7 111 | **/ 112 | private Date addDay(Date date) { 113 | LOGGER.debug("已增加日期"); 114 | Calendar startDT = Calendar.getInstance(); 115 | startDT.setTime(date); 116 | startDT.add(Calendar.DAY_OF_MONTH, 1); 117 | return startDT.getTime(); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/model/UserInfo.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.model; 2 | 3 | import com.alibaba.fastjson.annotation.JSONField; 4 | 5 | /** 6 | * 用户. 7 | * 8 | * @author ScienJus 9 | * @author Liang Ding 10 | * @date 2015/12/24. 11 | */ 12 | public class UserInfo { 13 | 14 | private Birthday birthday; 15 | 16 | private String phone; 17 | 18 | private String occupation; 19 | 20 | private String college; 21 | 22 | private String uin; 23 | 24 | private int blood; 25 | 26 | private String lnick; //签名 27 | 28 | private String homepage; 29 | 30 | @JSONField(name = "vip_info") 31 | private int vipInfo; 32 | 33 | private String city; 34 | 35 | private String country; 36 | 37 | private String province; 38 | 39 | private String personal; 40 | 41 | private int shengxiao; 42 | 43 | private String nick; 44 | 45 | private String email; 46 | 47 | private String account; 48 | 49 | private String gender; 50 | 51 | private String mobile; 52 | 53 | public Birthday getBirthday() { 54 | return birthday; 55 | } 56 | 57 | public void setBirthday(Birthday birthday) { 58 | this.birthday = birthday; 59 | } 60 | 61 | public String getPhone() { 62 | return phone; 63 | } 64 | 65 | public void setPhone(String phone) { 66 | this.phone = phone; 67 | } 68 | 69 | public String getOccupation() { 70 | return occupation; 71 | } 72 | 73 | public void setOccupation(String occupation) { 74 | this.occupation = occupation; 75 | } 76 | 77 | public String getCollege() { 78 | return college; 79 | } 80 | 81 | public void setCollege(String college) { 82 | this.college = college; 83 | } 84 | 85 | public String getUin() { 86 | return uin; 87 | } 88 | 89 | public void setUin(String uin) { 90 | this.uin = uin; 91 | } 92 | 93 | public int getBlood() { 94 | return blood; 95 | } 96 | 97 | public void setBlood(int blood) { 98 | this.blood = blood; 99 | } 100 | 101 | public String getLnick() { 102 | return lnick; 103 | } 104 | 105 | public void setLnick(String lnick) { 106 | this.lnick = lnick; 107 | } 108 | 109 | public String getHomepage() { 110 | return homepage; 111 | } 112 | 113 | public void setHomepage(String homepage) { 114 | this.homepage = homepage; 115 | } 116 | 117 | public int getVipInfo() { 118 | return vipInfo; 119 | } 120 | 121 | public void setVipInfo(int vipInfo) { 122 | this.vipInfo = vipInfo; 123 | } 124 | 125 | public String getCity() { 126 | return city; 127 | } 128 | 129 | public void setCity(String city) { 130 | this.city = city; 131 | } 132 | 133 | public String getCountry() { 134 | return country; 135 | } 136 | 137 | public void setCountry(String country) { 138 | this.country = country; 139 | } 140 | 141 | public String getProvince() { 142 | return province; 143 | } 144 | 145 | public void setProvince(String province) { 146 | this.province = province; 147 | } 148 | 149 | public String getPersonal() { 150 | return personal; 151 | } 152 | 153 | public void setPersonal(String personal) { 154 | this.personal = personal; 155 | } 156 | 157 | public int getShengxiao() { 158 | return shengxiao; 159 | } 160 | 161 | public void setShengxiao(int shengxiao) { 162 | this.shengxiao = shengxiao; 163 | } 164 | 165 | public String getNick() { 166 | return nick; 167 | } 168 | 169 | public void setNick(String nick) { 170 | this.nick = nick; 171 | } 172 | 173 | public String getEmail() { 174 | return email; 175 | } 176 | 177 | public void setEmail(String email) { 178 | this.email = email; 179 | } 180 | 181 | public String getAccount() { 182 | return account; 183 | } 184 | 185 | public void setAccount(String account) { 186 | this.account = account; 187 | } 188 | 189 | public String getGender() { 190 | return gender; 191 | } 192 | 193 | public void setGender(String gender) { 194 | this.gender = gender; 195 | } 196 | 197 | public String getMobile() { 198 | return mobile; 199 | } 200 | 201 | public void setMobile(String mobile) { 202 | this.mobile = mobile; 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/constant/ApiURL.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.constant; 2 | 3 | /** 4 | * Api的请求地址和Referer 5 | * 6 | * @author ScienJus 7 | * @date 2015/12/19 8 | */ 9 | public enum ApiURL { 10 | /** 11 | * 12 | */ 13 | GET_QR_CODE( 14 | "https://ssl.ptlogin2.qq.com/ptqrshow?appid=501004106&e=0&l=M&s=5&d=72&v=4&t=0.1", 15 | "" 16 | ), 17 | VERIFY_QR_CODE( 18 | "https://ssl.ptlogin2.qq.com/ptqrlogin?" + 19 | "ptqrtoken={1}&webqq_type=10&remember_uin=1&login2qq=1&aid=501004106&" + 20 | "u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&" + 21 | "ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&0-0-157510&" + 22 | "mibao_css=m_webqq&t=undefined&g=1&js_type=0&js_ver=10184&login_sig=&pt_randsalt=3", 23 | "https://ui.ptlogin2.qq.com/cgi-bin/login?" + 24 | "daid=164&target=self&style=16&mibao_css=m_webqq&appid=501004106&enable_qlogin=0&no_verifyimg=1&" + 25 | "s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=20131024001" 26 | ), 27 | GET_PTWEBQQ( 28 | "{1}", 29 | null 30 | ), 31 | GET_VFWEBQQ( 32 | "http://s.web2.qq.com/api/getvfwebqq?ptwebqq={1}&clientid=53999199&psessionid=&t=0.1", 33 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 34 | ), 35 | GET_UIN_AND_PSESSIONID( 36 | "http://d1.web2.qq.com/channel/login2", 37 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 38 | ), 39 | GET_GROUP_LIST( 40 | "http://s.web2.qq.com/api/get_group_name_list_mask2", 41 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 42 | ), 43 | POLL_MESSAGE( 44 | "http://d1.web2.qq.com/channel/poll2", 45 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 46 | ), 47 | SEND_MESSAGE_TO_GROUP( 48 | "http://d1.web2.qq.com/channel/send_qun_msg2", 49 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 50 | ), 51 | GET_FRIEND_LIST( 52 | "http://s.web2.qq.com/api/get_user_friends2", 53 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 54 | ), 55 | SEND_MESSAGE_TO_FRIEND( 56 | "http://d1.web2.qq.com/channel/send_buddy_msg2", 57 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 58 | ), 59 | GET_DISCUSS_LIST( 60 | "http://s.web2.qq.com/api/get_discus_list?clientid=53999199&psessionid={1}&vfwebqq={2}&t=0.1", 61 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 62 | ), 63 | SEND_MESSAGE_TO_DISCUSS( 64 | "http://d1.web2.qq.com/channel/send_discu_msg2", 65 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 66 | ), 67 | GET_ACCOUNT_INFO( 68 | "http://s.web2.qq.com/api/get_self_info2?t=0.1", 69 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 70 | ), 71 | GET_RECENT_LIST( 72 | "http://d1.web2.qq.com/channel/get_recent_list2", 73 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 74 | ), 75 | GET_FRIEND_STATUS( 76 | "http://d1.web2.qq.com/channel/get_online_buddies2?vfwebqq={1}&clientid=53999199&psessionid={2}&t=0.1", 77 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 78 | ), 79 | GET_GROUP_INFO( 80 | "http://s.web2.qq.com/api/get_group_info_ext2?gcode={1}&vfwebqq={2}&t=0.1", 81 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 82 | ), 83 | GET_QQ_BY_ID( 84 | "http://s.web2.qq.com/api/get_friend_uin2?tuin={1}&type=1&vfwebqq={2}&t=0.1", 85 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 86 | ), 87 | GET_DISCUSS_INFO( 88 | "http://d1.web2.qq.com/channel/get_discu_info?did={1}&vfwebqq={2}&clientid=53999199&psessionid={3}&t=0.1", 89 | "http://d1.web2.qq.com/proxy.html?v=20151105001&callback=1&id=2" 90 | ), 91 | GET_FRIEND_INFO( 92 | "http://s.web2.qq.com/api/get_friend_info2?tuin={1}&vfwebqq={2}&clientid=53999199&psessionid={3}&t=0.1", 93 | "http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1" 94 | ); 95 | 96 | public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36"; 97 | 98 | private String url; 99 | 100 | private String referer; 101 | 102 | ApiURL(String url, String referer) { 103 | this.url = url; 104 | this.referer = referer; 105 | } 106 | 107 | public String getUrl() { 108 | return url; 109 | } 110 | 111 | 112 | public String getReferer() { 113 | return referer; 114 | } 115 | 116 | public String buildUrl(Object... params) { 117 | int i = 1; 118 | String url = this.url; 119 | for (Object param : params) { 120 | url = url.replace("{" + i++ + "}", param.toString()); 121 | } 122 | return url; 123 | } 124 | 125 | public String getOrigin() { 126 | return this.url.substring(0, url.lastIndexOf("/")); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | import com.alibaba.fastjson.JSONObject; 2 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 3 | import top.itning.curriculum.CurriculumClient; 4 | import top.itning.util.PropertiesUtil; 5 | import top.itning.weather.entity.Weather; 6 | import top.itning.weather.entity.WeatherData; 7 | import top.itning.weather.entity.WeatherInfo; 8 | 9 | import java.text.SimpleDateFormat; 10 | import java.util.Calendar; 11 | import java.util.Date; 12 | import java.util.Locale; 13 | import java.util.concurrent.ScheduledThreadPoolExecutor; 14 | import java.util.concurrent.ThreadFactory; 15 | import java.util.concurrent.TimeUnit; 16 | 17 | 18 | public class Test { 19 | private static final String URL = "http://www.sojson.com/open/api/weather/json.shtml?city=宾县"; 20 | private static final String DATA = "{\"date\":\"20171106\",\"message\":\"Success !\",\"status\":200,\"city\":\"宾县\",\"count\":2,\"data\":{\"shidu\":\"51%\",\"pm25\":146.0,\"pm10\":154.0,\"quality\":\"中度污染\",\"wendu\":\"11\",\"ganmao\":\"儿童、老年人及心脏、呼吸系统疾病患者人群应减少长时间或高强度户外锻炼,一般人群适量减少户外运动\",\"yesterday\":{\"date\":\"05日星期日\",\"sunrise\":\"06:14\",\"high\":\"高温 10.0℃\",\"low\":\"低温 3.0℃\",\"sunset\":\"16:13\",\"aqi\":126.0,\"fx\":\"西南风\",\"fl\":\"4-5级\",\"type\":\"晴\",\"notice\":\"天气干燥,请适当增加室内湿度\"},\"forecast\":[{\"date\":\"06日星期一\",\"sunrise\":\"06:15\",\"high\":\"高温 12.0℃\",\"low\":\"低温 0.0℃\",\"sunset\":\"16:11\",\"aqi\":139.0,\"fx\":\"西南风\",\"fl\":\"3-4级\",\"type\":\"多云\",\"notice\":\"今日多云,骑上单车去看看世界吧\"},{\"date\":\"07日星期二\",\"sunrise\":\"06:17\",\"high\":\"高温 6.0℃\",\"low\":\"低温 -4.0℃\",\"sunset\":\"16:10\",\"aqi\":158.0,\"fx\":\"北风\",\"fl\":\"3-4级\",\"type\":\"阵雨\",\"notice\":\"愿雨后清新的空气给您带来好心情!\"},{\"date\":\"08日星期三\",\"sunrise\":\"06:18\",\"high\":\"高温 4.0℃\",\"low\":\"低温 -5.0℃\",\"sunset\":\"16:09\",\"aqi\":109.0,\"fx\":\"西风\",\"fl\":\"3-4级\",\"type\":\"多云\",\"notice\":\"今日多云,骑上单车去看看世界吧\"},{\"date\":\"09日星期四\",\"sunrise\":\"06:19\",\"high\":\"高温 6.0℃\",\"low\":\"低温 -4.0℃\",\"sunset\":\"16:07\",\"aqi\":122.0,\"fx\":\"西南风\",\"fl\":\"3-4级\",\"type\":\"多云\",\"notice\":\"绵绵的云朵,形状千变万化\"},{\"date\":\"10日星期五\",\"sunrise\":\"06:21\",\"high\":\"高温 0.0℃\",\"low\":\"低温 -10.0℃\",\"sunset\":\"16:06\",\"aqi\":90.0,\"fx\":\"西风\",\"fl\":\"3-4级\",\"type\":\"阵雪\",\"notice\":\"出现降雪的区域,会给出行带来不便\"}]}}"; 21 | 22 | @org.junit.Test 23 | public void test() { 24 | /* Client build = Client.pooled().maxPerRoute(5).maxTotal(10).build(); 25 | Session session = build.session(); 26 | String body = session.get(URL).text().getBody(); 27 | JSONObject jsonObject=JSONObject.parseObject(body); 28 | Weather weather = JSONObject.toJavaObject(jsonObject, Weather.class); 29 | System.out.println(weather);*/ 30 | JSONObject jsonObject = JSONObject.parseObject(DATA); 31 | Weather weatherInfo = JSONObject.toJavaObject(jsonObject, Weather.class); 32 | if (200 == weatherInfo.getStatus()) { 33 | WeatherData data = weatherInfo.getData(); 34 | WeatherInfo info = data.getForecast()[0]; 35 | String msg = "今天是" + 36 | new SimpleDateFormat("yyyy年MM月dd日 EE ").format(new Date()) + 37 | "\n" + weatherInfo.getCity() + "天气:\n湿度:" + data.getShidu() + 38 | "\nPM2.5:" + data.getPm25() + "\n可吸入颗粒物:" + data.getPm10() + "\n" + 39 | data.getQuality() + " " + data.getGanmao() + 40 | "\n日出:" + info.getSunrise() + "\t日落:" + info.getSunset() + "\n" + info.getLow() + " " + info.getHigh() + "\n" + 41 | info.getType() + " " + info.getFx() + " " + info.getFl(); 42 | System.out.println(msg); 43 | } 44 | } 45 | 46 | @org.junit.Test 47 | public void tests() { 48 | String classInfo = CurriculumClient.getClassInfo(); 49 | System.out.println(classInfo.equals("")); 50 | } 51 | 52 | @org.junit.Test 53 | public void testss() { 54 | String ee = new SimpleDateFormat("EE").format(new Date()); 55 | System.out.println(ee); 56 | } 57 | 58 | @org.junit.Test 59 | public void testsss() throws InterruptedException { 60 | String classInfo = CurriculumClient.getClassInfo(); 61 | System.out.println(classInfo); 62 | /* Calendar calendar = Calendar.getInstance(); 63 | for(int i=1;i<8;i++){ 64 | calendar.set(Calendar.DAY_OF_WEEK, i); 65 | //第一次执行定时任务的时间 66 | Date date = calendar.getTime(); 67 | System.out.println(new SimpleDateFormat("EE", Locale.US).format(date)); 68 | }*/ 69 | } 70 | 71 | @org.junit.Test 72 | public void testa() throws InterruptedException { 73 | Calendar calendar = Calendar.getInstance(); 74 | calendar.set(Calendar.HOUR_OF_DAY, 19); 75 | calendar.set(Calendar.MINUTE, 16); 76 | calendar.set(Calendar.SECOND, 0); 77 | //第一次执行定时任务的时间 78 | Date date = calendar.getTime(); 79 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); 80 | ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2, namedThreadFactory); 81 | scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> { 82 | System.out.println("111"); 83 | }, date.getTime(), 500, TimeUnit.MILLISECONDS); 84 | Thread.sleep(99999999); 85 | } 86 | 87 | @org.junit.Test 88 | public void testb() throws InterruptedException { 89 | String adminName = PropertiesUtil.getValueByKey("groupName", "configurationInfo.properties"); 90 | System.out.println(adminName); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/top/itning/webqq/client/SmartQQClient.java: -------------------------------------------------------------------------------- 1 | package top.itning.webqq.client; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 7 | import net.dongliu.requests.Client; 8 | import net.dongliu.requests.HeadOnlyRequestBuilder; 9 | import net.dongliu.requests.Response; 10 | import net.dongliu.requests.Session; 11 | import net.dongliu.requests.exception.RequestException; 12 | import net.dongliu.requests.struct.Cookie; 13 | import org.apache.log4j.Logger; 14 | import top.itning.webqq.callback.MessageCallback; 15 | import top.itning.webqq.constant.ApiURL; 16 | import top.itning.webqq.model.*; 17 | 18 | import java.io.Closeable; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.net.SocketTimeoutException; 22 | import java.nio.charset.StandardCharsets; 23 | import java.util.*; 24 | import java.util.concurrent.ScheduledThreadPoolExecutor; 25 | import java.util.concurrent.ThreadFactory; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /*** 29 | * 30 | * @author : ning 31 | * @version : 1.0.0 32 | * @date : 2017/11/7 33 | **/ 34 | public class SmartQQClient implements Closeable { 35 | /** 36 | * 分隔符 37 | */ 38 | private static final String SPLIT_SYMBOL = "','"; 39 | /** 40 | * 失败码 41 | */ 42 | private static final int NOT_FOUNT_CODE = 404; 43 | /** 44 | * 成功码 45 | */ 46 | private static final int SUCCESS_CODE = 200; 47 | /** 48 | * 日志 49 | */ 50 | private static final Logger LOGGER = Logger.getLogger(SmartQQClient.class); 51 | 52 | /** 53 | * 发生ngnix 404 时的重试次数 54 | */ 55 | private static int retryTimesOnFailed = 3; 56 | 57 | /** 58 | * 消息id,这个好像可以随便设置,所以设成全局的 59 | */ 60 | private static long MESSAGE_ID = 43690001; 61 | 62 | /** 63 | * 客户端id,固定的 64 | */ 65 | private static final long CLIENT_ID = 53999199; 66 | 67 | /** 68 | * 消息发送失败重发次数 69 | */ 70 | private static final long RETRY_TIMES = 5; 71 | 72 | /** 73 | * 客户端 74 | */ 75 | private Client client; 76 | 77 | /** 78 | * 会话 79 | */ 80 | private Session session; 81 | 82 | /** 83 | * 二维码令牌 84 | */ 85 | private String qrsig; 86 | 87 | /** 88 | * 鉴权参数 89 | */ 90 | private String ptwebqq; 91 | 92 | private String vfwebqq; 93 | private long uin; 94 | private String psessionid; 95 | 96 | /** 97 | * 线程开关 98 | */ 99 | private volatile boolean pollStarted; 100 | 101 | 102 | public SmartQQClient(final MessageCallback callback) { 103 | this.client = Client.pooled().maxPerRoute(5).maxTotal(10).build(); 104 | this.session = client.session(); 105 | login(); 106 | if (callback != null) { 107 | this.pollStarted = true; 108 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); 109 | ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2, namedThreadFactory); 110 | scheduledThreadPoolExecutor.scheduleAtFixedRate( 111 | () -> { 112 | if (!pollStarted) { 113 | return; 114 | } 115 | try { 116 | pollMessage(callback); 117 | } catch (RequestException e) { 118 | //忽略SocketTimeoutException 119 | if (!(e.getCause() instanceof SocketTimeoutException)) { 120 | LOGGER.error(e.getMessage()); 121 | } 122 | } catch (Exception e) { 123 | LOGGER.error(e.getMessage()); 124 | } 125 | }, 126 | 0, 500, TimeUnit.MILLISECONDS); 127 | } 128 | } 129 | 130 | /** 131 | * 登录 132 | */ 133 | private void login() { 134 | getQRCode(); 135 | String url = verifyQRCode(); 136 | getPtwebqq(url); 137 | getVfwebqq(); 138 | getUinAndPsessionid(); 139 | getFriendStatus(); //修复Api返回码[103]的问题 140 | //登录成功欢迎语 141 | UserInfo userInfo = getAccountInfo(); 142 | LOGGER.info(userInfo.getNick() + ",欢迎!"); 143 | } 144 | 145 | /** 146 | * 登录流程1:获取二维码 147 | */ 148 | private void getQRCode() { 149 | LOGGER.debug("开始获取二维码"); 150 | 151 | //本地存储二维码图片 152 | String filePath; 153 | try { 154 | filePath = new File("qrcode.png").getCanonicalPath(); 155 | } catch (IOException e) { 156 | throw new IllegalStateException("二维码保存失败"); 157 | } 158 | Response response = session.get(ApiURL.GET_QR_CODE.getUrl()) 159 | .addHeader("User-Agent", ApiURL.USER_AGENT) 160 | .file(filePath); 161 | for (Cookie cookie : response.getCookies()) { 162 | if ("qrsig".equals(cookie.getName())) { 163 | qrsig = cookie.getValue(); 164 | break; 165 | } 166 | } 167 | LOGGER.info("二维码已保存在 " + filePath + " 文件中,请打开手机QQ并扫描二维码"); 168 | } 169 | 170 | /** 171 | * 用于生成ptqrtoken的哈希函数 172 | * 173 | * @param s qrsig 174 | * @return 哈希数 175 | */ 176 | private static int hash33(String s) { 177 | int e = 0, n = s.length(); 178 | for (int i = 0; n > i; ++i) { 179 | e += (e << 5) + s.charAt(i); 180 | } 181 | return 2147483647 & e; 182 | } 183 | 184 | /** 185 | * 登录流程2:校验二维码 186 | * 187 | * @return url 188 | */ 189 | private String verifyQRCode() { 190 | LOGGER.debug("等待扫描二维码"); 191 | 192 | //阻塞直到确认二维码认证成功 193 | while (true) { 194 | sleep(1); 195 | Response response = get(ApiURL.VERIFY_QR_CODE, hash33(qrsig)); 196 | String result = response.getBody(); 197 | if (result.contains("成功")) { 198 | for (String content : result.split(SPLIT_SYMBOL)) { 199 | if (content.startsWith("http")) { 200 | LOGGER.info("正在登录,请稍后"); 201 | return content; 202 | } 203 | } 204 | } else if (result.contains("已失效")) { 205 | LOGGER.info("二维码已失效,尝试重新获取二维码"); 206 | getQRCode(); 207 | } 208 | } 209 | 210 | } 211 | 212 | /** 213 | * 登录流程3:获取ptwebqq 214 | * 215 | * @param url url 216 | */ 217 | private void getPtwebqq(String url) { 218 | LOGGER.debug("开始获取ptwebqq"); 219 | 220 | Response response = get(ApiURL.GET_PTWEBQQ, url); 221 | this.ptwebqq = response.getCookies().get("ptwebqq").iterator().next().getValue(); 222 | } 223 | 224 | /** 225 | * 登录流程4:获取vfwebqq 226 | */ 227 | private void getVfwebqq() { 228 | LOGGER.debug("开始获取vfwebqq"); 229 | 230 | Response response = get(ApiURL.GET_VFWEBQQ, ptwebqq); 231 | int retryTimes4Vfwebqq = retryTimesOnFailed; 232 | while (response.getStatusCode() == NOT_FOUNT_CODE && retryTimes4Vfwebqq > 0) { 233 | response = get(ApiURL.GET_VFWEBQQ, ptwebqq); 234 | retryTimes4Vfwebqq--; 235 | } 236 | this.vfwebqq = getJsonObjectResult(response).getString("vfwebqq"); 237 | } 238 | 239 | /** 240 | * 登录流程5:获取uin和psessionid 241 | */ 242 | private void getUinAndPsessionid() { 243 | LOGGER.debug("开始获取uin和psessionid"); 244 | 245 | JSONObject r = new JSONObject(); 246 | r.put("ptwebqq", ptwebqq); 247 | r.put("clientid", CLIENT_ID); 248 | r.put("psessionid", ""); 249 | r.put("status", "online"); 250 | 251 | Response response = post(ApiURL.GET_UIN_AND_PSESSIONID, r); 252 | JSONObject result = getJsonObjectResult(response); 253 | this.psessionid = result.getString("psessionid"); 254 | this.uin = result.getLongValue("uin"); 255 | } 256 | 257 | /** 258 | * 获取群列表 259 | * 260 | * @return 群列表 261 | */ 262 | public List getGroupList() { 263 | LOGGER.debug("开始获取群列表"); 264 | 265 | JSONObject r = new JSONObject(); 266 | r.put("vfwebqq", vfwebqq); 267 | r.put("hash", hash()); 268 | 269 | Response response = post(ApiURL.GET_GROUP_LIST, r); 270 | int retryTimes4getGroupList = retryTimesOnFailed; 271 | while (response.getStatusCode() == NOT_FOUNT_CODE && retryTimes4getGroupList > 0) { 272 | response = post(ApiURL.GET_GROUP_LIST, r); 273 | retryTimes4getGroupList--; 274 | } 275 | JSONObject result = getJsonObjectResult(response); 276 | return JSON.parseArray(result.getJSONArray("gnamelist").toJSONString(), Group.class); 277 | } 278 | 279 | /** 280 | * 拉取消息 281 | * 282 | * @param callback 获取消息后的回调 283 | */ 284 | private void pollMessage(MessageCallback callback) { 285 | LOGGER.debug("开始接收消息"); 286 | 287 | JSONObject r = new JSONObject(); 288 | r.put("ptwebqq", ptwebqq); 289 | r.put("clientid", CLIENT_ID); 290 | r.put("psessionid", psessionid); 291 | r.put("key", ""); 292 | 293 | Response response = post(ApiURL.POLL_MESSAGE, r); 294 | JSONArray array = getJsonArrayResult(response); 295 | for (int i = 0; array != null && i < array.size(); i++) { 296 | JSONObject message = array.getJSONObject(i); 297 | String type = message.getString("poll_type"); 298 | if ("message".equals(type)) { 299 | callback.onMessage(new Message(message.getJSONObject("value"))); 300 | } else if ("group_message".equals(type)) { 301 | callback.onGroupMessage(new GroupMessage(message.getJSONObject("value"))); 302 | } else if ("discu_message".equals(type)) { 303 | callback.onDiscussMessage(new DiscussMessage(message.getJSONObject("value"))); 304 | } 305 | } 306 | } 307 | 308 | /** 309 | * 发送群消息 310 | * 311 | * @param groupId 群id 312 | * @param msg 消息内容 313 | */ 314 | public void sendMessageToGroup(long groupId, String msg) { 315 | LOGGER.debug("开始发送群消息"); 316 | 317 | JSONObject r = new JSONObject(); 318 | r.put("group_uin", groupId); 319 | //注意这里虽然格式是Json,但是实际是String 320 | r.put("content", JSON.toJSONString(Arrays.asList(msg, Arrays.asList("font", Font.DEFAULT_FONT)))); 321 | r.put("face", 573); 322 | r.put("clientid", CLIENT_ID); 323 | r.put("msg_id", MESSAGE_ID++); 324 | r.put("psessionid", psessionid); 325 | 326 | Response response = postWithRetry(ApiURL.SEND_MESSAGE_TO_GROUP, r); 327 | checkSendMsgResult(response); 328 | } 329 | 330 | /** 331 | * 发送讨论组消息 332 | * 333 | * @param discussId 讨论组id 334 | * @param msg 消息内容 335 | */ 336 | public void sendMessageToDiscuss(long discussId, String msg) { 337 | LOGGER.debug("开始发送讨论组消息"); 338 | 339 | JSONObject r = new JSONObject(); 340 | r.put("did", discussId); 341 | //注意这里虽然格式是Json,但是实际是String 342 | r.put("content", JSON.toJSONString(Arrays.asList(msg, Arrays.asList("font", Font.DEFAULT_FONT)))); 343 | r.put("face", 573); 344 | r.put("clientid", CLIENT_ID); 345 | r.put("msg_id", MESSAGE_ID++); 346 | r.put("psessionid", psessionid); 347 | 348 | Response response = postWithRetry(ApiURL.SEND_MESSAGE_TO_DISCUSS, r); 349 | checkSendMsgResult(response); 350 | } 351 | 352 | /** 353 | * 发送消息 354 | * 355 | * @param friendId 好友id 356 | * @param msg 消息内容 357 | */ 358 | public void sendMessageToFriend(long friendId, String msg) { 359 | LOGGER.debug("开始发送消息"); 360 | 361 | JSONObject r = new JSONObject(); 362 | r.put("to", friendId); 363 | //注意这里虽然格式是Json,但是实际是String 364 | r.put("content", JSON.toJSONString(Arrays.asList(msg, Arrays.asList("font", Font.DEFAULT_FONT)))); 365 | r.put("face", 573); 366 | r.put("clientid", CLIENT_ID); 367 | r.put("msg_id", MESSAGE_ID++); 368 | r.put("psessionid", psessionid); 369 | 370 | Response response = postWithRetry(ApiURL.SEND_MESSAGE_TO_FRIEND, r); 371 | checkSendMsgResult(response); 372 | } 373 | 374 | /** 375 | * 获得讨论组列表 376 | * 377 | * @return 讨论组列表 378 | */ 379 | public List getDiscussList() { 380 | LOGGER.debug("开始获取讨论组列表"); 381 | 382 | Response response = get(ApiURL.GET_DISCUSS_LIST, psessionid, vfwebqq); 383 | return JSON.parseArray(getJsonObjectResult(response).getJSONArray("dnamelist").toJSONString(), Discuss.class); 384 | } 385 | 386 | /** 387 | * 获得好友列表(包含分组信息) 388 | * 389 | * @return 好友列表 390 | */ 391 | public List getFriendListWithCategory() { 392 | LOGGER.debug("开始获取好友列表"); 393 | 394 | JSONObject r = new JSONObject(); 395 | r.put("vfwebqq", vfwebqq); 396 | r.put("hash", hash()); 397 | 398 | Response response = post(ApiURL.GET_FRIEND_LIST, r); 399 | JSONObject result = getJsonObjectResult(response); 400 | //获得好友信息 401 | Map friendMap = parseFriendMap(result); 402 | //获得分组 403 | JSONArray categories = result.getJSONArray("categories"); 404 | Map categoryMap = new HashMap<>(16); 405 | categoryMap.put(0, Category.defaultCategory()); 406 | for (int i = 0; categories != null && i < categories.size(); i++) { 407 | Category category = categories.getObject(i, Category.class); 408 | categoryMap.put(category.getIndex(), category); 409 | } 410 | JSONArray friends = result.getJSONArray("friends"); 411 | for (int i = 0; friends != null && i < friends.size(); i++) { 412 | JSONObject item = friends.getJSONObject(i); 413 | Friend friend = friendMap.get(item.getLongValue("uin")); 414 | categoryMap.get(item.getIntValue("categories")).addFriend(friend); 415 | } 416 | return new ArrayList<>(categoryMap.values()); 417 | } 418 | 419 | /** 420 | * 获取好友列表 421 | * 422 | * @return 好友列表 423 | */ 424 | public List getFriendList() { 425 | LOGGER.debug("开始获取好友列表"); 426 | 427 | JSONObject r = new JSONObject(); 428 | r.put("vfwebqq", vfwebqq); 429 | r.put("hash", hash()); 430 | 431 | Response response = post(ApiURL.GET_FRIEND_LIST, r); 432 | return new ArrayList<>(parseFriendMap(getJsonObjectResult(response)).values()); 433 | } 434 | 435 | /** 436 | * 将json解析为好友列表 437 | * 438 | * @param result json 439 | * @return 好友列表 440 | */ 441 | private static Map parseFriendMap(JSONObject result) { 442 | Map friendMap = new HashMap<>(16); 443 | JSONArray info = result.getJSONArray("info"); 444 | for (int i = 0; info != null && i < info.size(); i++) { 445 | JSONObject item = info.getJSONObject(i); 446 | Friend friend = new Friend(); 447 | friend.setUserId(item.getLongValue("uin")); 448 | friend.setNickname(item.getString("nick")); 449 | friendMap.put(friend.getUserId(), friend); 450 | } 451 | JSONArray marknames = result.getJSONArray("marknames"); 452 | for (int i = 0; marknames != null && i < marknames.size(); i++) { 453 | JSONObject item = marknames.getJSONObject(i); 454 | friendMap.get(item.getLongValue("uin")).setMarkname(item.getString("markname")); 455 | } 456 | JSONArray vipinfo = result.getJSONArray("vipinfo"); 457 | for (int i = 0; vipinfo != null && i < vipinfo.size(); i++) { 458 | JSONObject item = vipinfo.getJSONObject(i); 459 | Friend friend = friendMap.get(item.getLongValue("u")); 460 | friend.setVip(item.getIntValue("is_vip") == 1); 461 | friend.setVipLevel(item.getIntValue("vip_level")); 462 | } 463 | return friendMap; 464 | } 465 | 466 | /** 467 | * 获得当前登录用户的详细信息 468 | * 469 | * @return 用户的详细信息 470 | */ 471 | public UserInfo getAccountInfo() { 472 | LOGGER.debug("开始获取登录用户信息"); 473 | 474 | Response response = get(ApiURL.GET_ACCOUNT_INFO); 475 | int retryTimes4AccountInfo = retryTimesOnFailed; 476 | while (response.getStatusCode() == NOT_FOUNT_CODE && retryTimes4AccountInfo > 0) { 477 | response = get(ApiURL.GET_ACCOUNT_INFO); 478 | retryTimes4AccountInfo--; 479 | } 480 | return JSON.parseObject(getJsonObjectResult(response).toJSONString(), UserInfo.class); 481 | } 482 | 483 | /** 484 | * 获得好友的详细信息 485 | * 486 | * @return 好友的详细信息 487 | */ 488 | public UserInfo getFriendInfo(long friendId) { 489 | LOGGER.debug("开始获取好友信息"); 490 | 491 | Response response = get(ApiURL.GET_FRIEND_INFO, friendId, vfwebqq, psessionid); 492 | return JSON.parseObject(getJsonObjectResult(response).toJSONString(), UserInfo.class); 493 | } 494 | 495 | /** 496 | * 获得最近会话列表 497 | * 498 | * @return 最近会话列表 499 | */ 500 | public List getRecentList() { 501 | LOGGER.debug("开始获取最近会话列表"); 502 | 503 | JSONObject r = new JSONObject(); 504 | r.put("vfwebqq", vfwebqq); 505 | r.put("clientid", CLIENT_ID); 506 | r.put("psessionid", ""); 507 | 508 | Response response = post(ApiURL.GET_RECENT_LIST, r); 509 | return JSON.parseArray(getJsonArrayResult(response).toJSONString(), Recent.class); 510 | } 511 | 512 | /** 513 | * 获得qq号 514 | * 515 | * @param friendId 用户id 516 | * @return qq号 517 | */ 518 | public long getQQById(long friendId) { 519 | LOGGER.debug("开始获取QQ号"); 520 | Response response = get(ApiURL.GET_QQ_BY_ID, friendId, vfwebqq); 521 | return getJsonObjectResult(response).getLongValue("account"); 522 | } 523 | 524 | /** 525 | * 获得好友的qq号 526 | * 527 | * @param friend 好友对象 528 | * @return qq号 529 | */ 530 | public long getQQById(Friend friend) { 531 | return getQQById(friend.getUserId()); 532 | } 533 | 534 | /** 535 | * 获得群友的qq号 536 | * 537 | * @param user 群友对象 538 | * @return qq号 539 | */ 540 | public long getQQById(GroupUser user) { 541 | return getQQById(user.getUin()); 542 | } 543 | 544 | /** 545 | * 获得讨论组成员的qq号 546 | * 547 | * @param user 讨论组成员对象 548 | * @return qq号 549 | */ 550 | public long getQQById(DiscussUser user) { 551 | return getQQById(user.getUin()); 552 | } 553 | 554 | /** 555 | * 获得私聊消息发送者的qq号 556 | * 557 | * @param msg 私聊消息 558 | * @return qq号 559 | */ 560 | public long getQQById(Message msg) { 561 | return getQQById(msg.getUserId()); 562 | } 563 | 564 | /** 565 | * 获得群消息发送者的qq号 566 | * 567 | * @param msg 群消息 568 | * @return qq号 569 | */ 570 | public long getQQById(GroupMessage msg) { 571 | return getQQById(msg.getUserId()); 572 | } 573 | 574 | /** 575 | * 获得讨论组消息发送者的qq号 576 | * 577 | * @param msg 讨论组消息 578 | * @return qq号 579 | */ 580 | public long getQQById(DiscussMessage msg) { 581 | return getQQById(msg.getUserId()); 582 | } 583 | 584 | /** 585 | * 获得登录状态 586 | * 587 | * @return 登录状态 588 | */ 589 | public List getFriendStatus() { 590 | LOGGER.debug("开始获取好友状态"); 591 | Response response = get(ApiURL.GET_FRIEND_STATUS, vfwebqq, psessionid); 592 | return JSON.parseArray(getJsonArrayResult(response).toJSONString(), FriendStatus.class); 593 | } 594 | 595 | /** 596 | * 获得群的详细信息 597 | * 598 | * @param groupCode 群编号 599 | * @return 详细信息 600 | */ 601 | public GroupInfo getGroupInfo(long groupCode) { 602 | LOGGER.debug("开始获取群资料"); 603 | 604 | Response response = get(ApiURL.GET_GROUP_INFO, groupCode, vfwebqq); 605 | JSONObject result = getJsonObjectResult(response); 606 | GroupInfo groupInfo = result.getObject("ginfo", GroupInfo.class); 607 | //获得群成员信息 608 | Map groupUserMap = new HashMap<>(16); 609 | JSONArray minfo = result.getJSONArray("minfo"); 610 | for (int i = 0; minfo != null && i < minfo.size(); i++) { 611 | GroupUser groupUser = minfo.getObject(i, GroupUser.class); 612 | groupUserMap.put(groupUser.getUin(), groupUser); 613 | groupInfo.addUser(groupUser); 614 | } 615 | JSONArray stats = result.getJSONArray("stats"); 616 | for (int i = 0; stats != null && i < stats.size(); i++) { 617 | JSONObject item = stats.getJSONObject(i); 618 | GroupUser groupUser = groupUserMap.get(item.getLongValue("uin")); 619 | groupUser.setClientType(item.getIntValue("client_type")); 620 | groupUser.setStatus(item.getIntValue("stat")); 621 | } 622 | JSONArray cards = result.getJSONArray("cards"); 623 | for (int i = 0; cards != null && i < cards.size(); i++) { 624 | JSONObject item = cards.getJSONObject(i); 625 | groupUserMap.get(item.getLongValue("muin")).setCard(item.getString("card")); 626 | } 627 | JSONArray vipinfo = result.getJSONArray("vipinfo"); 628 | for (int i = 0; vipinfo != null && i < vipinfo.size(); i++) { 629 | JSONObject item = vipinfo.getJSONObject(i); 630 | GroupUser groupUser = groupUserMap.get(item.getLongValue("u")); 631 | groupUser.setVip(item.getIntValue("is_vip") == 1); 632 | groupUser.setVipLevel(item.getIntValue("vip_level")); 633 | } 634 | return groupInfo; 635 | } 636 | 637 | /** 638 | * 获得讨论组的详细信息 639 | * 640 | * @param discussId 讨论组id 641 | * @return 详细信息 642 | */ 643 | public DiscussInfo getDiscussInfo(long discussId) { 644 | LOGGER.debug("开始获取讨论组资料"); 645 | 646 | Response response = get(ApiURL.GET_DISCUSS_INFO, discussId, vfwebqq, psessionid); 647 | JSONObject result = getJsonObjectResult(response); 648 | DiscussInfo discussInfo = result.getObject("info", DiscussInfo.class); 649 | //获得讨论组成员信息 650 | Map discussUserMap = new HashMap<>(16); 651 | JSONArray minfo = result.getJSONArray("mem_info"); 652 | for (int i = 0; minfo != null && i < minfo.size(); i++) { 653 | DiscussUser discussUser = minfo.getObject(i, DiscussUser.class); 654 | discussUserMap.put(discussUser.getUin(), discussUser); 655 | discussInfo.addUser(discussUser); 656 | } 657 | JSONArray stats = result.getJSONArray("mem_status"); 658 | for (int i = 0; stats != null && i < stats.size(); i++) { 659 | JSONObject item = stats.getJSONObject(i); 660 | DiscussUser discussUser = discussUserMap.get(item.getLongValue("uin")); 661 | discussUser.setClientType(item.getIntValue("client_type")); 662 | discussUser.setStatus(item.getString("status")); 663 | } 664 | return discussInfo; 665 | } 666 | 667 | /** 668 | * 发送get请求 669 | * 670 | * @param url url 671 | * @param params 参数 672 | * @return 结果 673 | */ 674 | private Response get(ApiURL url, Object... params) { 675 | HeadOnlyRequestBuilder request = session.get(url.buildUrl(params)) 676 | .addHeader("User-Agent", ApiURL.USER_AGENT); 677 | if (url.getReferer() != null) { 678 | request.addHeader("Referer", url.getReferer()); 679 | } 680 | return request.text(StandardCharsets.UTF_8); 681 | } 682 | 683 | /** 684 | * 发送post请求 685 | * 686 | * @param url url 687 | * @param r JSONObject 688 | * @return Response 689 | */ 690 | private Response post(ApiURL url, JSONObject r) { 691 | return session.post(url.getUrl()) 692 | .addHeader("User-Agent", ApiURL.USER_AGENT) 693 | .addHeader("Referer", url.getReferer()) 694 | .addHeader("Origin", url.getOrigin()) 695 | .addForm("r", r.toJSONString()) 696 | .text(StandardCharsets.UTF_8); 697 | } 698 | 699 | /** 700 | * 发送post请求,失败时重试 701 | * 702 | * @param url url 703 | * @param r JSONObject 704 | * @return Response 705 | */ 706 | private Response postWithRetry(ApiURL url, JSONObject r) { 707 | int times = 0; 708 | Response response; 709 | do { 710 | response = post(url, r); 711 | times++; 712 | } while (times < RETRY_TIMES && response.getStatusCode() != SUCCESS_CODE); 713 | return response; 714 | } 715 | 716 | /** 717 | * 获取返回json的result字段(JSONObject类型) 718 | * 719 | * @param response Response 720 | * @return JSONObject 721 | */ 722 | private static JSONObject getJsonObjectResult(Response response) { 723 | return getResponseJson(response).getJSONObject("result"); 724 | } 725 | 726 | /** 727 | * 获取返回json的result字段(JSONArray类型) 728 | * 729 | * @param response Response 730 | * @return JSONArray 731 | */ 732 | private static JSONArray getJsonArrayResult(Response response) { 733 | return getResponseJson(response).getJSONArray("result"); 734 | } 735 | 736 | /** 737 | * 检查消息是否发送成功 738 | * 739 | * @param response Response 740 | */ 741 | private static void checkSendMsgResult(Response response) { 742 | if (response.getStatusCode() != SUCCESS_CODE) { 743 | LOGGER.error(String.format("发送失败,Http返回码[%d]", response.getStatusCode())); 744 | } 745 | JSONObject json = JSON.parseObject(response.getBody()); 746 | Integer errCode = json.getInteger("retcode"); 747 | if (errCode != null && errCode == 0) { 748 | LOGGER.debug("发送成功"); 749 | } else { 750 | LOGGER.error(String.format("发送失败,Api返回码[%d]", json.getInteger("retcode"))); 751 | } 752 | } 753 | 754 | /** 755 | * 检验Json返回结果 756 | * 757 | * @param response Response 758 | * @return JSONObject 759 | */ 760 | private static JSONObject getResponseJson(Response response) { 761 | if (response.getStatusCode() != SUCCESS_CODE) { 762 | throw new RequestException(String.format("请求失败,Http返回码[%d]", response.getStatusCode())); 763 | } 764 | JSONObject json = JSON.parseObject(response.getBody()); 765 | Integer retCode = json.getInteger("retcode"); 766 | if (retCode == null) { 767 | throw new RequestException("请求失败,Api返回异常 retCode == null"); 768 | } else if (retCode != 0) { 769 | switch (retCode) { 770 | case 103: { 771 | LOGGER.error("请求失败,Api返回码[103]。你需要进入http://w.qq.com,检查是否能正常接收消息。如果可以的话点击[设置]->[退出登录]后查看是否恢复正常"); 772 | break; 773 | } 774 | case 100100: { 775 | LOGGER.debug("请求失败,Api返回码[100100]"); 776 | break; 777 | } 778 | default: { 779 | throw new RequestException(String.format("请求失败,Api返回码[%d]", retCode)); 780 | } 781 | } 782 | } 783 | return json; 784 | } 785 | 786 | /** 787 | * hash加密方法 788 | * 789 | * @return hash加密 790 | */ 791 | private String hash() { 792 | return hash(uin, ptwebqq); 793 | } 794 | 795 | /** 796 | * 线程暂停 797 | * 798 | * @param seconds 时间 799 | */ 800 | private static void sleep(long seconds) { 801 | try { 802 | Thread.sleep(seconds * 1000); 803 | } catch (InterruptedException e) { 804 | //忽略InterruptedException 805 | } 806 | } 807 | 808 | /** 809 | * hash加密方法 810 | * 811 | * @param x x 812 | * @param k k 813 | * @return String 814 | */ 815 | private static String hash(long x, String k) { 816 | int[] n = new int[4]; 817 | for (int t = 0; t < k.length(); t++) { 818 | n[t % 4] ^= k.charAt(t); 819 | } 820 | String[] u = {"EC", "OK"}; 821 | long[] v = new long[4]; 822 | v[0] = x >> 24 & 255 ^ u[0].charAt(0); 823 | v[1] = x >> 16 & 255 ^ u[0].charAt(1); 824 | v[2] = x >> 8 & 255 ^ u[1].charAt(0); 825 | v[3] = x & 255 ^ u[1].charAt(1); 826 | 827 | long[] u1 = new long[8]; 828 | 829 | for (int t = 0; t < 8; t++) { 830 | u1[t] = t % 2 == 0 ? n[t >> 1] : v[t >> 1]; 831 | } 832 | 833 | String[] n1 = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; 834 | StringBuilder v1 = new StringBuilder(); 835 | for (long aU1 : u1) { 836 | v1.append(n1[(int) ((aU1 >> 4) & 15)]); 837 | v1.append(n1[(int) (aU1 & 15)]); 838 | } 839 | return v1.toString(); 840 | } 841 | 842 | @Override 843 | public void close() throws IOException { 844 | this.pollStarted = false; 845 | if (this.client != null) { 846 | this.client.close(); 847 | } 848 | } 849 | } 850 | --------------------------------------------------------------------------------