Skip to content

Auto captcha for ios and android currently #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/captcha-solver.tflite
Binary file not shown.
2 changes: 1 addition & 1 deletion assets/flutter_i18n/zh_CN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ setting:
rrrilac: "开发支持:电费查询"
ray: "设计:开屏画面 / 支持:iOS 发行商 & 搭子课表 / 开发指导:情侣课表功能开发指导"
shadowyingyi: "支持:两次鸽子公众号宣传"
stalomeow: "设计:首页时间轴 / 开发:异步登录"
stalomeow: "设计:首页时间轴 / 开发:异步登录 & 验证码预测"
xeonds: "设计:设置页面 / 开发:XDU Planet"
xiue233: "开发:Android 小部件和拼接"
wirsbf: "开发:修复调课未按预期进行"
Expand Down
2 changes: 1 addition & 1 deletion assets/flutter_i18n/zh_TW.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ setting:
rrrilac: 開發支援:電費查詢
ray: 設計:開屏畫面 / 支援:iOS 發行商 & 搭子課表 / 開發指導:情侶課表功能開發指導
shadowyingyi: 支援:兩次鴿子公眾號宣傳
stalomeow: 設計:首頁時間軸 / 開發:非同步登入
stalomeow: 設計:首頁時間軸 / 開發:非同步登入 & 验证码預測
xeonds: 設計:設定頁面 / 開發:XDU Planet
xiue233: 開發:Android 小部件和拼接
wirsbf: 開發:修復調課未按預期進行
Expand Down
50 changes: 50 additions & 0 deletions lib/page/public_widget/captcha_input_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,56 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:watermeter/page/public_widget/toast.dart';
import 'package:image/image.dart' as img;
import 'package:tflite_flutter/tflite_flutter.dart';

class DigitCaptchaClientProvider {
static const String _interpreterAssetName = 'assets/captcha-solver.tflite';

static Future<String> infer(List<int> imageData) async {
// Ref: https://github.com/stalomeow/captcha-solver

img.Image image = img.decodeImage(Uint8List.fromList(imageData))!;
image = img.grayscale(image);
image = image.convert(
format: img.Format.float32, numChannels: 1); // 0-256 to 0-1

int dim2 = image.height;
int dim3 = image.width ~/ 4;

var input = List.filled(dim2 * dim3, 0.0)
.reshape<double>([1, dim2, dim3, 1]) as List<List<List<List<double>>>>;
var output =
List.filled(9, 0.0).reshape<double>([1, 9]) as List<List<double>>;

final interpreter = await Interpreter.fromAsset(_interpreterAssetName);
List<int> nums = [];

// Four numbers
for (int i = 0; i < 4; i++) {
for (int y = 0; y < dim2; y++) {
for (int x = 0; x < dim3; x++) {
input[0][y][x][0] = image.getPixel(x + dim3 * i, y).r.toDouble();
}
}

interpreter.run(input, output);
nums.add(_argmax(output[0]) + 1);
}

return nums.join('');
}

static int _argmax(List<double> list) {
int result = 0;
for (int i = 1; i < list.length; i++) {
if (list[i] > list[result]) {
result = i;
}
}
return result;
}
}

class CaptchaInputDialog extends StatelessWidget {
final TextEditingController _captchaController = TextEditingController();
Expand Down
129 changes: 71 additions & 58 deletions lib/repository/xidian_ids/payment_session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:dio/dio.dart';
import 'package:encrypt/encrypt.dart';
import 'package:watermeter/model/xidian_ids/electricity.dart';
import 'package:watermeter/page/login/jc_captcha.dart';
import 'package:watermeter/page/public_widget/captcha_input_dialog.dart';
import 'package:watermeter/repository/logger.dart';
import 'package:get/get.dart';
import 'package:watermeter/repository/network_session.dart';
Expand Down Expand Up @@ -352,68 +353,80 @@ xh5zeF9usFgtdabgACU/cQIDAQAB

await dio.get(nextStop[0]!.replaceAll('"', ""));

// New stuff, captcha code...
var picture = await dio
.get(
"https://payment.xidian.edu.cn/NetWorkUI/authImage",
options: Options(responseType: ResponseType.bytes),
)
.then((data) => data.data);
String checkCode = await captchaFunction(picture);

log.info("[PaymentSession][getOwe] checkcode is $checkCode");

var value = await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/checkUserInfo",
data: {
"p_Userid": rsaEncrypt(electricityAccount()),
"p_Password": rsaEncrypt(password),
"checkCode": rsaEncrypt(checkCode),
"factorycode": factorycode,
},
);
if ((value.statusCode ?? 0) >= 300 && (value.statusCode ?? 0) < 400) {
log.info(
"[PaymentSession][getOwe] "
"Newbee detected.",
String lastErrorMessage = "";
for (int retry = 5; retry > 0; retry--) {
// New stuff, captcha code...
var picture = await dio
.get(
"https://payment.xidian.edu.cn/NetWorkUI/authImage",
options: Options(responseType: ResponseType.bytes),
)
.then((data) => data.data);

String checkCode = retry == 1
? await captchaFunction(picture) // The last try
: await DigitCaptchaClientProvider.infer(picture);

log.info("[PaymentSession][getOwe] checkcode is $checkCode");

var value = await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/checkUserInfo",
data: {
"p_Userid": rsaEncrypt(electricityAccount()),
"p_Password": rsaEncrypt(password),
"checkCode": rsaEncrypt(checkCode),
"factorycode": factorycode,
},
);
bool isPostGraduate =
await PersonalInfoSession().checkWhetherPostgraduate();

await dio.post(value.headers["location"]![0]).then((value) async {
await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/perfectUserinfo",
data: {
"tel": isPostGraduate
? await PersonalInfoSession()
.getInformationFromYjspt(onlyPhone: true)
: await PersonalInfoSession()
.getInformationEhall(onlyPhone: true),
"email": "${preference.getString(preference.Preference.idsAccount)}"
"@stu.mail.xidian.edu.cn",
},
options: Options(contentType: Headers.jsonContentType),
);
}).then((value) async {
value = await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/checkUserInfo",
data: {
"p_Userid": electricityAccount(),
"p_Password": password,
"factorycode": factorycode,
},

if ((value.statusCode ?? 0) >= 300 && (value.statusCode ?? 0) < 400) {
log.info(
"[PaymentSession][getOwe] "
"Newbee detected.",
);
});
}
bool isPostGraduate =
await PersonalInfoSession().checkWhetherPostgraduate();

await dio.post(value.headers["location"]![0]).then((value) async {
await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/perfectUserinfo",
data: {
"tel": isPostGraduate
? await PersonalInfoSession()
.getInformationFromYjspt(onlyPhone: true)
: await PersonalInfoSession()
.getInformationEhall(onlyPhone: true),
"email":
"${preference.getString(preference.Preference.idsAccount)}"
"@stu.mail.xidian.edu.cn",
},
options: Options(contentType: Headers.jsonContentType),
);
}).then((value) async {
value = await dio.post(
"https://payment.xidian.edu.cn/NetWorkUI/checkUserInfo",
data: {
"p_Userid": electricityAccount(),
"p_Password": password,
"factorycode": factorycode,
},
);
});
}

var decodeData = jsonDecode(value.data);
if (decodeData["returncode"] == "ERROR") {
throw NotInitalizedException(decodeData["returnmsg"]);
var decodeData = jsonDecode(value.data);
if (decodeData["returncode"] == "ERROR") {
lastErrorMessage = decodeData["returnmsg"];
continue;
}

return (
decodeData["roomList"][0].toString().split('@')[0],
decodeData["liveid"].toString(),
);
}
return (
decodeData["roomList"][0].toString().split('@')[0],
decodeData["liveid"].toString(),
);

throw NotInitalizedException(lastErrorMessage);
}

Future<void> getElectricity((String, String) fetched) => dio.post(
Expand Down
1 change: 1 addition & 0 deletions linux/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
tflite_flutter
)

set(PLUGIN_BUNDLED_LIBRARIES)
Expand Down
16 changes: 16 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.0"
quiver:
dependency: transitive
description:
name: quiver
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
url: "https://pub.dev"
source: hosted
version: "3.2.2"
receive_sharing_intent:
dependency: "direct main"
description:
Expand Down Expand Up @@ -1391,6 +1399,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
tflite_flutter:
dependency: "direct main"
description:
name: tflite_flutter
sha256: "48e6fde2ad97162bb66a16a142f4c4698add9e8cd397ce9d1cc7451b55537ac1"
url: "https://pub.dev"
source: hosted
version: "0.11.0"
timelines:
dependency: "direct main"
description:
Expand Down
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies:
encrypt: ^5.0.1
talker_flutter: ^4.4.1
talker_dio_logger: ^4.4.1
tflite_flutter: ^0.11.0
synchronized: ^3.1.0+1
# Stroage Cache
shared_preferences: ^2.0.15
Expand Down Expand Up @@ -111,5 +112,6 @@ flutter:
- assets/flutter_i18n/zh_CN.yaml
- assets/flutter_i18n/zh_TW.yaml
- assets/Classtable-Empty.png
- assets/captcha-solver.tflite
# - assets/flutter_i18n/zh_SG.json

1 change: 1 addition & 0 deletions windows/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
tflite_flutter
)

set(PLUGIN_BUNDLED_LIBRARIES)
Expand Down