We aim to automate the card sign in process for HEZ, and more!
This project is highly specific. It only solves one problem. I'm sure you know what I am talking about if you already know about our situation.
That's because it was wrong when the target program was developed.
- Python 3.8 or 3.10, it might work on other versions, but it's not tested.
- Python's Tkinter support (some installations don't come with one).
- Windows 7 (x64) (Unsupported on other platforms, untested on other versions). But anyway, why would you care about the server side's OS? There's no choice.
- The target program might update, breaking this program. But I've already warned you, this project is highly specific.
- The tested compiler is MinGW-w64/GCC 8.2.0 and 12.1.0, with mingw32-make.
- CMake version 3.23.2
- This project uses library Better SQLite3 Multiple Cipher. Tested version is v1.4.8.
- Nlohmann JSON, version 3.10.5
- The icon for the executable was provided by Aha soft.
- Boost C++ libraries, version 1.80.0.
Thanks for all the tools, libraries and resources the community provides so generously!
-
On the build machine, clone the source code into a directory.
-
Check and double check that your client can access the server.
-
Make sure that your editor supports UTF-8. Specifically, avoid using
notepadto edit files because it might introduce strange BOMs, breaking the config file. -
On a terminal,
cdinto that directory. -
Create a
include/directory containing headers of the external library. Place SQL headers ininclude/and JSON headers ininclude/nlohmann. Kindly putsqlite3mc_x64.dllin the root directory of the project. -
Configure and compile using cmake. If your linker complains about spirit.rc.res is not an object, try explicitly telling cmake that you're using
windresas the RC compiler. Also check boost path. Because Boost.asio is quite large a library, we recommend precompiling it to accelerate the build. However, this isn't strictly necessary. -
Create config files as shown below.
-
You will need to copy the following files into your removable disk:
cppser/spiritd.exeman.json, the server config file.- All the DLLs in the same directory as your
g++.exe(or the compiler you use) sqlite3mc_x64.dllandcppsper/libspirit.dll
-
Plug in your media into the server (You will need some techniques for this.)
-
Open task manager, kill
LockMouse.exeand restartexplorer.exe. -
Copy the files mentioned above into a folder.
-
Go to task scheduler, add a task that starts
spiritd.exe2 minutes after system boot. -
Reboot to check.
-
When asked about the firewall, allow all access.
The file is named man.json. A template looks like this:
{
"serv_port": 8303,
"gs_port": 10031,
"dbname": "D:/singin/localData.db",
"passwd": "123",
"url_stu_new": "http://127.0.0.1//Services/SmartBoard/SmartBoardLoadSingInStudentNew/json",
"intro": "Spirit version 3.1 is up!",
"watchdog_poll": 15,
"retry_wait": 3,
"keep_logs": 3,
"timeout": 5,
"auto_watchdog": true,
"simul_limit": 90,
"local_limit": 60
}- serv_port: The port that we should use.
- gs_port: The port of the GS executable
- dbname: The path to the database.
- passwd: The password required to access the database.
- url_stu_new: The URL to post for student info.
- intro: The first line to appear in singer.log, customizable.
- watchdog_poll: The seconds watchdog waits before checking for lessons, quit, etc.
- retry_wait: If the previous attempt to auto sign in failed because of network or database error,
wait for
retry_waitseconds before retrying. - keep_logs: The number of logs to keep before rotating over to an older one.
- timeout: The timeout in seconds for the request to
url_stu_new. - auto_watchdog: Specifies the state of the watchdog when the program launches. If set to false,
watchdog will be paused. You can control the state with
dbguiat any time. - simul_limit: Controls the threshold for web-based sign in attempts. The first lesson ending in less than
simul_limitseconds but more thanlocal_limitwill be subjected to web-based sign in. - local_limit: Lessons ending in less than
local_limitseconds will be resorted to local DB based sign in.
Note that the program requires simul_limit >= local_limit, because local sign in is supposed to be a kind
of last resort.
This file is called cli.json, with a template given below:
{
"port": 8303,
"defhost": "127.0.0.1",
"buffsize": 2048,
"defmachine": "BJ303",
"timeout": 10,
"yearbook": {
"sp": "spirit",
"dp": "deophius"
}
}- port: The port that we use to communicate with the server.
- defhost: The default host to inquire for information in the GUI.
- buffsize: The size of the buffer into which the response will be received. This shouldn't be too small (<1024) and would preferentially be a power of 2.
- defmachine: The default machine ID in the GUI.
- timeout: Timeout before the GUI decides that the server is not responding.
- yearbook: An optional object holding the abbreviation mapping used by the quick find box.
For example, if the config looks exactly like the one shown above, you can type
spin the quick find box and press enter. If the namespiritis in the list of absent people, it will automatically be selected. This object is optional! If not present, the quick find function will be disabled.
Press enter to submit the machine name and host IP info.
If you want to use a non-default machine IP or machine name, you can press
tab to switch to different input boxes.
- For the first 9 lessons that appear on the list, you can press
1to9to choose them.1is for the one on the top of the list. - Press
entershould have the same effect as pressing the OK button. - Press
pto trigger "pause watchdog". - Press
rto trigger "resume watchdog". - Press
gto trigger "get latest news".
-
To quickly search through the list of absent people, use the quickfind input box. The box grabs the focus on default, so this is completely keyboard friendly.
You need to specify the abbreviation mapping in the config file. See the
cli.jsonsection for details. -
Different from the previous page, if you want to trigger the buttons with your keyboard, you need to use a alt key combination. See the in-program hints.
We currently use green, orange and red to indicate the status of our program.
- Green means that the last operation was successful.
- Orange means that a lengthy operation, usually a request to the server, is being performed.
- Red means that the last operation resulted in an error.
This is a simple, stateless, UDP-based protocol that uses JSON as the "mime type".
The client sends a request to the server. The request must include a command param, with
additional data.
The server's response always contains a success param, set to true if operation succeeds and
false otherwise. If success is false, then there will always be a what param providing
a brief explanation of the error.
The names of the commands should be self-explaining.
Here we make a listing of the implemented commands and their syntax:
{"command": "report_absent", "sessid": 1}
-> {"success": true, "name": ["xxx", "xxx", ...]} on success
-> {"success": false, "what": "error description"} on failuresessid is a non-negative integer. The order of lessons is assigned according to the sequence they
appear in the database. On the client side, this can be determined from the response of today_info.
The server implements range checks on sessid.
{"command": "write_record", "name": ["xxx", "yyy"], "sessid":1}
-> {"success": true}
-> {"success": false, "what": "error description"}name is a list of Chinese names. sessid is same as above.
The server catches all SQL errors and returns them as what if database operations fail.
Because we use JSON as the "mime type", the name strings should be UTF-8 encoded.
{"command": "restart_gs"}
-> {"success": true}
-> {"success": false, "what": "error description"}Restarts GS. If the GS machine is not up, we will inform you in the what string. If GS doesn't recognize our command, you will also be notified. In this case, maybe it's up to you to figure out the new command string to use.
{"command":"today_info", "machine": "NJ303"}
-> {"success": true, "end": ["07:20:00", "08:30:00"]}
-> {"success": false, "what":"Wrong machine", "machine":"BJ303"}
-> {"success": false, "what": "error description"}Gets the lessons there are today. Different from versions 2.x, broadcast is no longer used!
The client must know the IP of the server and its machine ID to access the server.
If the machine in the request matches that of the DB, returns the list of lessons represented
as their "endtimes" in the format shown above.
If these two don't match, returns the machine ID in the database along with an error message.
{"command": "quit_spirit"}
-> {"success": true}Exits the server. Normally, this will always return true, as reporting errors in the destructors is kind of difficult.
{"command": "flush_notice"}
-> {"success": true}
-> {"success": false, "what": "error description"}Using GS's port, we can inform it to get the latest notice immediately. This might be useful if too much network analysis breaks the network interface configuration and you cannot receive notifications at all.
For the error scenarios, see restart_gs.
{"command": "doggie_stick", "pause": true or false}
-> {"success": true}
-> {"success": false, "what": "..."} on errors, like a missing pause argument.Tells the watchdog to pause or resume, respectively. Usually atomic bool operations are noexcept, so we will simply return a success.
Good luck!