-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c3f5bdb
Showing
11 changed files
with
420 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
* | ||
!.gitignore | ||
!*/ | ||
!*.java | ||
!lib/* | ||
!config/config.example.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# 每轮查询的间隔(秒),建议设置为600以上 | ||
interval: 600 | ||
# 记录当前成绩的文件(JSON) | ||
record-file: grades-record.json | ||
|
||
# 需要查询的学生 | ||
students: | ||
- id: 学生1学号 | ||
password: 学生1密码 | ||
email: 学生1邮箱 | ||
- id: 学生2学号 | ||
password: 学生2密码 | ||
email: 学生2邮箱 | ||
# 更多的学生用类似格式添加 | ||
|
||
# 发件人邮箱设置(默认使用SSL/TLS) | ||
email: | ||
address: [email protected] # 邮箱地址 | ||
sender-name: 長門有希 # 发件人显示姓名 | ||
username: username # SMTP登录用户名 | ||
password: password # SMTP登录密码 | ||
server: smtp.example.com # SMTP服务器地址 | ||
port: 465 # SMTP服务器端口 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.yuki_nagato.csunotifier; | ||
|
||
import com.esotericsoftware.yamlbeans.YamlException; | ||
import com.esotericsoftware.yamlbeans.YamlReader; | ||
|
||
import java.io.*; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class Config { | ||
public final int interval; | ||
public final String recordFile; | ||
public final List<Map<String, String>> students; | ||
public final Map<String, String> email; | ||
|
||
public Config(File yamlFile) throws IOException { | ||
YamlReader reader; | ||
try { | ||
reader = new YamlReader(new InputStreamReader(new FileInputStream(yamlFile),"utf-8")); | ||
} | ||
catch (FileNotFoundException e) { | ||
System.err.println("配置文件 "+yamlFile.getPath()+" 未找到"); | ||
throw e; | ||
} | ||
try { | ||
Map<String, Object> map = (Map)reader.read(); | ||
reader.close(); | ||
interval = Integer.parseInt((String)map.get("interval")); | ||
recordFile = (String)map.get("record-file"); | ||
students = (List)map.get("students"); | ||
email = (Map)map.get("email"); | ||
} | ||
catch (YamlException | ClassCastException e) { | ||
System.err.println("配置文件 "+yamlFile.getPath()+" 格式错误"); | ||
throw e; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package com.yuki_nagato.csunotifier; | ||
|
||
import org.json.JSONObject; | ||
|
||
import java.util.Map; | ||
|
||
public class Grade { | ||
public final String course,orientationTerm,getTerm,processScore,examScore,mark,credit; | ||
public Grade(String course,String orientationTerm, String getTerm, String processScore, String examScore, String mark, String credit) { | ||
this.course = course; | ||
this.orientationTerm = orientationTerm; | ||
this.getTerm = getTerm; | ||
this.processScore = processScore; | ||
this.examScore = examScore; | ||
this.mark = mark; | ||
this.credit = credit; | ||
} | ||
public Grade(Map<String, String> map) { | ||
course = map.get("course"); | ||
orientationTerm = map.get("orientationTerm"); | ||
getTerm = map.get("getTerm"); | ||
processScore = map.get("processScore"); | ||
examScore = map.get("examScore"); | ||
mark = map.get("mark"); | ||
credit = map.get("credit"); | ||
} | ||
|
||
public JSONObject toJson() { | ||
JSONObject rst = new JSONObject(); | ||
rst.put("course", course); | ||
rst.put("orientationTerm", orientationTerm); | ||
rst.put("getTerm", getTerm); | ||
rst.put("processScore", processScore); | ||
rst.put("examScore", examScore); | ||
rst.put("mark", mark); | ||
rst.put("credit", credit); | ||
return rst; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if(obj.getClass()!=Grade.class) return false; | ||
Grade b = (Grade)obj; | ||
return course.equals(b.course) && | ||
orientationTerm.equals(b.orientationTerm) && | ||
getTerm.equals(b.getTerm) && | ||
processScore.equals(b.processScore) && | ||
examScore.equals(b.examScore) && | ||
mark.equals(b.mark) && | ||
credit.equals(b.credit); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return course.hashCode(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.yuki_nagato.csunotifier; | ||
|
||
import javax.mail.*; | ||
import javax.mail.internet.InternetAddress; | ||
import javax.mail.internet.MimeMessage; | ||
import java.io.UnsupportedEncodingException; | ||
import java.util.Properties; | ||
|
||
public class MailSender { | ||
private final InternetAddress from; | ||
private final String server, username, password, port, senderName; | ||
|
||
public MailSender(String address, String name, String server, String username, String password, String port) throws UnsupportedEncodingException { | ||
this.from = new InternetAddress(address, name, "utf-8"); | ||
this.server = server; | ||
this.username = username; | ||
this.password = password; | ||
this.port = port; | ||
this.senderName = name; | ||
} | ||
public void send(Student student, Diff diff) throws MessagingException, UnsupportedEncodingException { | ||
Properties props = new Properties(); | ||
props.put("mail.smtp.host", server); | ||
props.put("mail.smtp.port", port); | ||
props.put("mail.smtp.ssl.enable", "true"); | ||
props.put("mail.smtp.auth", "true"); | ||
Session session = Session.getInstance(props, new Authenticator() { | ||
@Override | ||
protected PasswordAuthentication getPasswordAuthentication() { | ||
return new PasswordAuthentication(username, password); | ||
} | ||
}); | ||
MimeMessage message = new MimeMessage(session); | ||
message.setFrom(from); | ||
message.setRecipient(Message.RecipientType.TO, new InternetAddress(student.email,student.getName(),"utf-8")); | ||
message.setSubject("中南大学新成绩通知","utf-8"); | ||
message.setContent(generateContent(student, diff), "text/html;charset=utf-8"); | ||
message.saveChanges(); | ||
Transport.send(message); | ||
} | ||
|
||
String generateContent(Student student, Diff diff) { | ||
StringBuilder rst = new StringBuilder(); | ||
rst.append("<p>").append(student.getName()).append("同学:</p>"); | ||
rst.append("<p>您有成绩变化:</p>"); | ||
if(!diff.additions.isEmpty()) { | ||
rst.append("<p>新增的成绩为</p>"); | ||
rst.append("<table border=\"1\"><tr><th>课程</th><th>初修学期</th><th>获得学期</th><th>过程成绩</th><th>期末成绩</th><th>成绩</th><th>学分</th></tr>"); | ||
for(Grade grade : diff.additions) { | ||
rst.append(String.format("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", grade.course, grade.orientationTerm, grade.getTerm, grade.processScore, grade.examScore, grade.mark, grade.credit)); | ||
} | ||
rst.append("</table>"); | ||
} | ||
if(!diff.deletions.isEmpty()) { | ||
rst.append("<p>消失的成绩为</p>"); | ||
rst.append("<table border=\"1\"><tr><th>课程</th><th>初修学期</th><th>获得学期</th><th>过程成绩</th><th>期末成绩</th><th>成绩</th><th>学分</th></tr>"); | ||
for(Grade grade : diff.deletions) { | ||
rst.append(String.format("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>", grade.course, grade.orientationTerm, grade.getTerm, grade.processScore, grade.examScore, grade.mark, grade.credit)); | ||
} | ||
rst.append("</table>"); | ||
} | ||
rst.append("<p>Yours sincerely,<br/>").append(senderName).append("</p>"); | ||
return rst.toString(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.yuki_nagato.csunotifier; | ||
|
||
import javax.mail.MessagingException; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
|
||
public class Main { | ||
public static void main(String[] args) throws IOException, InterruptedException { | ||
final Config cfg = new Config(new File("config/config.yaml")); | ||
Student.recordFile = new File(cfg.recordFile); | ||
Student.load(); | ||
MailSender sender = new MailSender(cfg.email.get("address"),cfg.email.get("sender-name"),cfg.email.get("server"),cfg.email.get("username"),cfg.email.get("password"),cfg.email.get("port")); | ||
while(true) { | ||
for(Map<String, String> student : cfg.students) { | ||
String id = student.get("id"), password = student.get("password"), email = student.get("email"); | ||
try { | ||
System.out.println("开始查询"+id); | ||
Student stu = new Student(id,password,email); | ||
Diff diff = stu.check(); | ||
if(diff.additions.isEmpty() && diff.deletions.isEmpty()) { | ||
System.out.println("没有变化"); | ||
} | ||
else { | ||
System.out.printf("新增%d个成绩,减少%d个成绩\n", diff.additions.size(), diff.deletions.size()); | ||
sender.send(stu,diff); | ||
System.out.println("邮件发送成功"); | ||
} | ||
} | ||
catch (MessagingException e) { | ||
System.err.println("邮件发送失败"); | ||
e.printStackTrace(); | ||
} | ||
catch (AuthenticationException e) { | ||
System.err.println("用户名或密码错误"); | ||
} | ||
catch (NullPointerException | IOException e) { | ||
System.err.println("关于"+id+"的配置可能有误"); | ||
e.printStackTrace(); | ||
} | ||
Thread.sleep(cfg.interval*1000/cfg.students.size()); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.yuki_nagato.csunotifier; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.net.*; | ||
import java.util.ArrayList; | ||
import java.util.Base64; | ||
import java.util.List; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class ScorePage { | ||
private final String content; | ||
|
||
public ScorePage(String username, String password) throws IOException, AuthenticationException { | ||
URL url = new URL("http://csujwc.its.csu.edu.cn/jsxsd/xk/LoginToXk"); | ||
HttpURLConnection connection = (HttpURLConnection)url.openConnection(); | ||
connection.setRequestMethod("POST"); | ||
connection.setInstanceFollowRedirects(false); | ||
connection.setDoOutput(true); | ||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); | ||
String encoded = Base64.getEncoder().encodeToString(username.getBytes("utf-8")) + | ||
"%%%" + | ||
Base64.getEncoder().encodeToString(password.getBytes("utf-8")); | ||
connection.getOutputStream().write(("encoded="+ URLEncoder.encode(encoded,"utf-8")).getBytes("utf-8")); | ||
if(connection.getResponseCode()!=302) { | ||
throw new AuthenticationException(); | ||
} | ||
List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); | ||
|
||
// get page | ||
url = new URL("http://csujwc.its.csu.edu.cn/jsxsd/kscj/yscjcx_list"); | ||
connection = (HttpURLConnection)url.openConnection(); | ||
for(String cookie : cookies) { | ||
connection.addRequestProperty("Cookie", cookie.substring(0,cookie.indexOf(';'))); | ||
} | ||
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); | ||
StringBuilder body = new StringBuilder(); | ||
String line; | ||
while((line = br.readLine())!=null) { | ||
body.append(line); | ||
body.append('\n'); | ||
} | ||
|
||
//exit | ||
url = new URL("http://csujwc.its.csu.edu.cn/jsxsd/xk/LoginToXk?method=exit"); | ||
connection = (HttpURLConnection)url.openConnection(); | ||
connection.setInstanceFollowRedirects(false); | ||
for(String cookie : cookies) { | ||
connection.addRequestProperty("Cookie", cookie.substring(0, cookie.indexOf(';'))); | ||
} | ||
connection.getResponseCode(); | ||
|
||
content = body.toString(); | ||
} | ||
public ArrayList<Grade> getGrades() { | ||
String pt = "<tr>.*?<td>.*?<td>(.*?)</td>.*?<td>(.*?)</td>.*?<td align=\"left\">(.*?)</td>.*?<td>(.*?)</td>.*?<td>(.*?)</td>.*?,700,500\\)\">(.*?)</a>.*?<td>(.*?)</td>"; | ||
Pattern pattern = Pattern.compile(pt, Pattern.DOTALL); | ||
Matcher m = pattern.matcher(content); | ||
ArrayList<Grade> rst = new ArrayList<>(); | ||
while(m.find()) { | ||
rst.add(new Grade(m.group(3),m.group(1),m.group(2),m.group(4),m.group(5),m.group(6),m.group(7))); | ||
} | ||
return rst; | ||
} | ||
public String getName() { | ||
String pt = " (.*)\\("; | ||
Pattern pattern = Pattern.compile(pt); | ||
Matcher m = pattern.matcher(content); | ||
m.find(); | ||
return m.group(1); | ||
} | ||
} | ||
|
||
class AuthenticationException extends Exception { | ||
AuthenticationException() { | ||
super("用户名或密码错误"); | ||
} | ||
} |
Oops, something went wrong.