Use shell scripts to automatically apply for a Beijing Entry Permit (outside the Sixth Ring Road)

problem background

Vehicles from other places need to apply for a "Beijing Entry Permit" to enter Beijing. If you do not apply for a permit, you will be captured by law enforcement equipment and will be fined 100 and 1 point will be deducted. At present, the only online processing channel is to download the "Beijing Traffic Police" app and add it after registration. Vehicles, you can apply for a Beijing entry permit for your car anytime, anywhere. Note that if there is an illegal record, you need to pay a fine before you can handle it. The online channel for paying fines is "Traffic Management 12123".

At the earliest time, the Beijing Entry Permit was only restricted to non-local vehicles entering the Fifth Ring Road, and it could be applied for 12 times a year, with a period of 7 days. Later, the scope was expanded to the Sixth Ring Road. At the beginning, the surveillance equipment was not in good condition, and it was still possible to enter. Now, it is not possible. Basically, as soon as one enters the Sixth Ring Road, one will be photographed. Do you just run around outside the sixth ring road? No, like some remote counties such as Changping, Yanqing, Shunyi, Miyun, Huairou, and Pinggu, although they are located outside the sixth ring road, there is also a ring in the urban area of ​​the county town. Foreign car owners like me who hang out around the county are very unfriendly, and they have to apply for a Beijing entry permit to go to shopping centers and movie theaters in the county on weekends. Perhaps because of listening to public opinion, it was later reformed, and the Beijing Entry Permit was changed into two types: within the sixth ring road and outside the sixth ring road: within the sixth ring road, there are still 12 times a year, 7 days each time; outside the sixth ring road, one time for 7 days but unlimited times . An exception is Tongzhou. Even though it is outside the Sixth Ring Road, it has to be handled within the Sixth Ring Road. After all, it is the sub-center of the city!

Although there is no limit to the number of times, it is not arbitrarily handled. The main rules are as follows:

  • Unpaid fines cannot be processed
  • After 12:00 noon, you cannot apply for the Beijing Entry Permit on the same day
  • Can apply in advance, up to 3 days in advance
  • One account can be attached to multiple vehicles, but only for one of them
  • If you have already applied for the Beijing Entry Permit outside the Sixth Ring Road, you can continue to apply for the Beijing Entry Permit inside the Sixth Ring Road. After the success, the Beijing Entry Permit outside the Sixth Ring Road will become invalid; otherwise, it is not necessary, because you can travel outside the Sixth Ring Road with the Beijing Entry Permit inside the Sixth Ring Road
  • If you apply for a Beijing Entry Permit that is not on the same day, you can cancel it, but there is only one chance to cancel it in a day, and you can't cancel it after you use it up
  • When the Beijing Entry Permit is valid on the last day, you can apply for a new Beijing Entry Permit, also known as renewal
  • It cannot be handled during special periods, such as during the epidemic control period (the article has been turned over, it is just an example)

The speed of online processing is relatively fast, and the results can be obtained in about a minute. But for amnesiacs like me, I often remember to apply for a Beijing entry permit just before leaving the house. Looking at the time, it is already past 12 noon, which is rather embarrassing.

At this time, I thought that it would be great to have a tool to apply for the Beijing Entry Permit on a regular basis. For example, every Monday morning, the Beijing Entry Permit for this week will be automatically processed.

solution

Once the requirements are clarified, let’s proceed to implement them.

grab bag

First of all, we need to understand the interface of the server, and we need to analyze the packets of "Beijing Traffic Police" by capturing packets.

Previously, I used Charles on mac to capture packets. Later, I happened to see a VNET App, which can capture packets without installing a certificate or starting an agent. I tried it and it was really easy to use:

Be careful not to enable packet capture during the login process, otherwise the login interface of "Beijing Traffic Police" will not be able to be adjusted. In addition, other requests can basically be caught, and two interfaces that we need to pay attention to are selected from them.

Obtain the current Beijing Entry Permit

This stateList interface can obtain the Beijing Entry Permit information of all vehicles under the user account. Some fields are required for the next step to apply for the Beijing Entry Permit. In addition, understanding the current Beijing Entry Permit status is also helpful for deciding whether to apply for a new Beijing Entry Permit.

header

> POST /pro//applyRecordController/stateList HTTP/1.1
> Host:jjz.jtgl.beijing.gov.cn
> Accept: */*
> Accept-Language:zh-CN,zh;q=0.8
> User-Agent:okhttp-okgo/jeasonlzy
> source:8724a2428c3f47358741f978fd082810
> authorization:f36abdfa-8878-46bf-91d9-5666f808e9a4
> Content-Type:application/json;charset=utf-8
> Connection:Keep-Alive
> Accept-Encoding:gzip
> Content-Length:97

More important are the source and authorization fields, the former identifies the device, and the latter identifies the user token, which is also one of the main grab objects and will be used in subsequent requests.

data

{
  "v": "3.4.1",
  "sfzmhm": "150121198603226428",
  "s-source": "bjjj-android",
  "timestamp": "1676016273000"
}

The json data sent during the request is useless for personal testing, and you can get a response even if you send it empty. It seems that the identity information is obtained through the authorization field of the http header.

response

view code

 
 

The returned json is relatively large, and is basically organized according to the structure of data->bzclxx[]->bzxx[], where data stores user information; bzclxx is an array of vehicles, storing information related to vehicles; bzxx is an array of Beijing entry permits, which are stored with certificate-related information.

The following is a brief description of the key fields:

  • data.sfzmhm: ID number of the account owner
  • bzclxx[].vId: The unique identifier of the vehicle, which will be used in subsequent applications
  • bzclxx[].hpzl: Unknown, it will be used in later applications
  • bzclxx[].hphm: Vehicle number plate, which will be used in subsequent applications
  • bzclxx[].bzxx[].blztmc: Beijing entry permit status, including but not limited to:
    • Approved (in effect)
    • Approved (to be effective)
    • Approved (expired)
    • Approved (obsolete)
    • under review
    • Failed (audit failed)
    • cancel processing
    • Cancelled
    • ......
  • bzclxx[].bzxx[].jsrxm: the name of the driver, which can be different from the account owner
  • bzclxx[].bzxx[].jszh: The driver's ID card, which must be consistent with the driver's name (that is, the person has been checked)
  • bzclxx[].bzxx[].jjzzlmc: Types of Beijing Entry Permit, mainly including:
    • Beijing Entry Permit (within the Sixth Ring Road)
    • Beijing Entry Permit (outside the Sixth Ring Road)
  • bzclxx[].bzxx[].yxqs: the first effective date of Beijing Entry Permit
  • bzclxx[].bzxx[].yxqz: the last effective date of Beijing Entry Permit
  • bzclxx[].bzxx[].sxsyts: the remaining validity period of the Beijing Entry Permit, in days

The latter three fields are only available in certain states. For example, yxqs and yxqz are valid when they are in effect, pending, and cancelled. szsyts is valid when they are in effect and pending. If the vehicle does not have a Beijing entry permit at all, the entire bzxx[] is null.

In theory, a vehicle can only have at most one Beijing Entry Permit, either inside the Sixth Ring Road or outside the Sixth Ring Road, but in some scenarios, the two permits can exist at the same time for a short time. The reason for the existence of the form. This scenario is mainly divided into two

  • On the premise of having the Beijing Entry Permit outside the Sixth Ring Road, if you successfully apply for the Beijing Entry Permit inside the Sixth Ring Road, the Beijing Entry Permit outside the Sixth Ring Road will be automatically invalidated, and the two will have a short coexistence period. After that, only the valid Sixth Ring Road will be retained Passed in Beijing
  • On the premise of having a Beijing Entry Permit, if the Beijing Entry Permit is valid on the last day, and the same type of Beijing Entry Permit is renewed at this time, the new certificate will be in a pending state, and the two will have a one-day coexistence period. After one day, the old certificate becomes invalid and the new certificate takes effect

Let's look at a practical example of the first scenario:

view code

 
 

It can be seen that the Beijing Entry Permit to be effective is actually placed in ecbzxx at the same level as bzxx, not in the bzxx array. It can be seen that the previous guess is wrong, although both bzxx and ecbzxx are designed as json arrays, In fact, they have at most one element, and if there is no corresponding information, keep null.

To make a digression, the interface of government affairs is particularly fond of naming fields with pinyin abbreviations. It takes a long time to guess the literal meaning, but it must be combined with the value situation. It is better if the value is a string. This kind of integer enumeration is impossible to guess at all. But looking at it from another angle, it plays a role of confusion and reinforcement, haha.

Apply for a new Beijing Entry Permit

Let's get to the point: this insertApplyRecord interface is used to apply for a Beijing entry permit.

header

From the packet capture data, the http header is exactly the same as the stateList request, refer to the previous section.

data

{
  "dabh": "null",
  "hphm": "津ADY1951",
  "hpzl": "52",
  "vId": "1479816562371952600",
  "jjdq": "海淀区",
  "jjlk": "00401",
  "jjlkmc": "京藏高速",
  "jjmd": "01",
  "jjmdmc": "自驾旅游",
  "jjrq": "2023-02-13",
  "jjzzl": "02",
  "jsrxm": "云海",
  "jszh": "150121198603226428",
  "sfzmhm": "150121198603226428",
  "xxdz": "百度大厦",
  "sqdzbdjd": 116.307393,
  "sqdzbdwd": 40.057771
}

The request data here is not dispensable, and the key ones are several fields such as hphm / hpzl / vId / jjrq / jsrxm / jszh / sfzmhm, etc. Most of them were introduced in the previous section, and the new ones are only jjrq One, indicating the start time of applying for the Beijing Entry Permit.

response

{
  "code": 200,
  "msg": "信息已提交,正在审核!",
  "data": [
    "温馨提示",
    "1、请务必在进京之前,查看进京通行证是否审核通过;",
    "2、若审核未通过,请按提示信息调整并重新申请;",
    "3、若审核通过,可在证件生效之前申请取消,每位注册用户每天仅有1次取消机会;",
    "4、在进京通行证未生效的情况下,外埠机动车禁止在限行区域内行驶;"
  ],
  "from": "v2"
}

When it succeeds, return the above result, call the stateList interface again, and the status of the Beijing Entry Permit will change to being reviewed. Several common error responses are listed below:

{
  "code": 500,
  "msg": "审核未通过,申请时间已超过中午12时,无法申请当日生效的进京证。",
  "data": "20015",
  "from": "v2"
}

The time is incorrect, apply for the same day Beijing entry permit after 12:00 noon, or jjrq uses a time in the past.

{
  "code": 500,
  "msg": "每个用户同一时间只能为一辆机动车申请办理进京证。",
  "data": "20005",
  "from": "v2"
}

If you have already applied for the Beijing Entry Permit for another car under your name, you cannot continue to apply.

Note that the ID card does not match, the vehicle Beijing entry permit has been processed by other accounts, etc., it will not return immediately, but will return after calling the stateList interface again after a period of time.

"bzxx": [
  {
    "vId": "1479816562371952600",
    "applyId": "831666307626696704",
    "blzt": 4,
    "blztmc": "失败(审核不通过)",
    "sxrqmc": "02月13日",
    "yxqs": "2023-02-13",
    "yxqz": null,
    "sxsyts": null,
    "jjzzl": "02",
    "jjzzlmc": "进京证(六环外)",
    "jjzh": null,
    "sqsj": "2023-02-12 23:04:59",
    "jsrxm": "云海",
    "jszh": "150121198603226428",
    "sfzmhm": null,
    "shsbyy": "10014",
    "shsbyyms": "审核未通过,驾驶人信息不正确,请核实后再次提交或到进京证办证窗口办理。",
    "tphtml": null,
    "hphm": "津ADY1951",
    "hpzl": "52",
    "vid": 1479816562371952600
  }
]

Like the above, it is because the ID card does not match the name.

{
  "msg": "目前办理业务人数较多,请稍后再试。",
  "code": 500
}

If the above information is returned, it means that there is a problem with the program, usually the header or data is not set correctly.

mock application

After the message is clear, it can be simulated with a shell script. The following is the script code:

view code

 
 

The complete code is on github:

GitHub - goodpaperman/jinjing365: deal with enter permits for Beijing 365 days

In addition to scripts, the code repository also contains configuration files (config.ini) and request templates (*.json).

The script is less than 300 lines, not too difficult to read, so I won’t explain it line by line here, just pick up a few key points to explain

jq

Because jq is essential to parse json, if it is missing on your system, the execution script will report a line of error:

please install jq before run this script, fatal error!

Other commands used such as curl and awk have also been checked to prevent missing dependencies in some special occasions.

There are two main uses of jq here, one is to parse the response content; the other is to generate the request content.

analyze

The parsing is relatively simple. For example, if you want to get the data.sfzmhm field, you can do it directly with one line of code:

local cardid=$(echo "${resp}" | jq -r '.data.sfzmhm') 

Used extensively in scripts, where the -r option can remove double quotes from strings.

built-in pipeline

It should be noted that jq supports built-in pipelines, which can be useful in some scenarios, for example:

local vsize=$(echo "${resp}" | jq -r '.data.bzclxx|length')
local psize=$(echo "${resp}" | jq -r ".data.bzclxx[${index}].bzxx|length")

Obtain the number of vehicles and the number of Beijing entry permits under a certain vehicle respectively, where length is a built-in function of jq, which can be used on the right side of the built-in pipeline.

Note that in the second example, the shell variable is directly embedded in the jq statement. At this time, double quotation marks should be used instead of single quotation marks, otherwise the shell variable cannot be expanded.

If you need to get all the values ​​in the array, it is not enough to use the built-in pipeline:

local vehicles=$(echo "${resp}" | jq -r '.data.bzclxx[].hphm')
local find=0
local index=0
# echo "${#vehicles}"
for var in ${vehicles}
do
    echo "try ${var} "
    if [ "${var}" = "${vehicle}" ]; then 
        # match
        find=1
        break; 
    fi
    index=$((index+1))
done

In the above example, the license plate numbers of all vehicles are obtained to the shell variable vehicles, and then used to  for..in traverse to find the vehicle index of the specified license plate.

built-in variable

The second usage of jq is to generate request content, where the built-in variables of jq are mainly used:

 local statereq=$(cat statereq.json | jq --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ v, sfzmhm: $sfzmhm, "s-source", timestamp: $timestamp }')

Pass the variable name and its value through --arg, for example,  --arg sfzmhm "${userid}" a variable named sfzmhm is generated for jq, and its value is the shell variable userid.

In the following jq script (specified by -c), you can use it directly $sfzmhm to refer to this variable. Note that $the prefix is ​​the jq variable, otherwise it is a literal value, indicating the field name of json.

Combined with the content of statereq.json, see what this code does:

{
  "v": "3.4.1",
  "sfzmhm": "",
  "s-source": "bjjj-android",
  "timestamp": ""
}

Read the json template and set the fields with specified values ​​(sfzmhm/timestamp), and those with specified field names but no specified values ​​(v/s-source) will continue to use the values ​​in the template, and those without specified field names will not appears in the final result.

The advantage of this replacement is that it is all handled by jq to avoid manually constructed strings that do not conform to the json syntax. There are a few points to note:

  • Variables in jq cannot be used outside jq
  • Variables in jq can only be referenced in the form of $xxx, ${xxx} cannot be quoted
  • If the field name in jq contains special symbols (such as s-source), you need to add double quotes when using it, otherwise jq will report an error

Builtin variables vs shell variables

Some readers are more careful and may ask, isn't it possible to use shell variables directly in jq in the second example of "built-in pipeline", then can you also use shell variables directly when constructing a request? Of course you can, taking the example in the previous section as an example, it is no problem to replace it with the following shell script:

# local statereq=$(cat statereq.json | jq --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") -c '{ v, sfzmhm: $sfzmhm, "s-source", timestamp: $timestamp }')

local statereq="{\"v\":\"3.4.1\",\"sfzmhm\":\"${userid}\",\"s-source\":\"bjjj-android\",\"timestamp\":\"$(date +%s000)\"}"

The content obtained is the same.

It can be seen that the entire json string needs to be surrounded by double quotes to contain shell variables, and a large number of json field names themselves have double quotes, which have to be escaped with backslashes.

In this way, there will be a lot of manual modification work, and the readability is relatively poor. Maybe this example is not very intuitive. Let’s compare the construction process of issuereq:

# local issuereq=$(cat issuereq.json | jq --arg hphm "${vehicle}" --arg hpzl "${hpzl}" --arg vid "${vid}" --arg jjrq "${issuedate}" --arg jsrxm "${drivername}" --arg jszh "${driverid}" --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") --arg data "${statereq}" -c '{ dabh, hphm: $hphm, hpzl: $hpzl, vId: $vid, jjdq, jjlk, jjlkmc, jjmd, jjmdmc, jjrq: $jjrq, jjzzl, jsrxm: $jsrxm, jszh: $jszh, sfzmhm: $sfzmhm, xxdz, sqdzbdjd, sqdzbdwd}')

local issuereq="{\"dabh\":\"null\",\"hphm\":\"${vehicle}\",\"hpzl\":\"${hpzl}\",\"vId\":\"${vid}\",\"jjdq\":\"海淀区\",\"jjlk\":\"00401\",\"jjlkmc\":\"京藏高速\",\"jjmd\":\"01\",\"jjmdmc\":\"自驾旅游\",\"jjrq\":\"${issuedate}\",\"jjzzl\":\"02\",\"jsrxm\":\"${drivername}\",\"jszh\":\"${driverid}\",\"sfzmhm\":\"${userid}\",\"xxdz\":\"百度大厦\",\"sqdzbdjd\":116.307393,\"sqdzbdwd\":40.057771}"

The quotation marks are so eye-catching. If one accidentally matches the wrong one, it will take a long time for people to find it.

In fact, the important reason for using jq built-in variables is that readability is only one aspect, and correctness is another aspect.

Assuming such a scenario, a new data field is added to issuereq, and the content is also json (in order to simplify the example, statereq is used directly), will the two methods be consistent?

local issuereq=$(cat issuereq.json | jq --arg hphm "${vehicle}" --arg hpzl "${hpzl}" --arg vid "${vid}" --arg jjrq "${issuedate}" --arg jsrxm "${drivername}" --arg jszh "${driverid}" --arg sfzmhm "${userid}" --arg timestamp $(date "+%s000") --arg data "${statereq}" -c '{ dabh, hphm: $hphm, hpzl: $hpzl, vId: $vid, jjdq, jjlk, jjlkmc, jjmd, jjmdmc, jjrq: $jjrq, jjzzl, jsrxm: $jsrxm, jszh: $jszh, sfzmhm: $sfzmhm, xxdz, sqdzbdjd, sqdzbdwd, data: $data }')
echo "issue req: ${issuereq}"

issuereq="{\"dabh\":\"null\",\"hphm\":\"${vehicle}\",\"hpzl\":\"${hpzl}\",\"vId\":\"${vid}\",\"jjdq\":\"海淀区\",\"jjlk\":\"00401\",\"jjlkmc\":\"京藏高速\",\"jjmd\":\"01\",\"jjmdmc\":\"自驾旅游\",\"jjrq\":\"${issuedate}\",\"jjzzl\":\"02\",\"jsrxm\":\"${drivername}\",\"jszh\":\"${driverid}\",\"sfzmhm\":\"${userid}\",\"xxdz\":\"百度大厦\",\"sqdzbdjd\":116.307393,\"sqdzbdwd\":40.057771,\"data\":\"${statereq}\"}"
echo "issue req: ${issuereq}"

Running the above script snippet results in the following:

issue req: {"dabh":"null","hphm":"津ADY1951","hpzl":"52","vId":"1480773467139342337","jjdq":"海淀区","jjlk":"00401","jjlkmc":"京藏高速","jjmd":"01","jjmdmc":"自驾旅游","jjrq":"2023-02-13","jjzzl":"02","jsrxm":"云海","jszh":"150121198603226428","sfzmhm":"150121198603226428","xxdz":"百度大厦","sqdzbdjd":116.307393,"sqdzbdwd":40.057771,"data":"{"v":"3.4.1","sfzmhm":"150121198603226428","s-source":"bjjj-android","timestamp":"1676214143000"}"}

issue req: {"dabh":"null","hphm":"津ADY1951","hpzl":"52","vId":"1480773467139342337","jjdq":"海淀区","jjlk":"00401","jjlkmc":"京藏高速","jjmd":"01","jjmdmc":"自驾旅游","jjrq":"2023-02-13","jjzzl":"02","jsrxm":"云海","jszh":"150121198603226428","sfzmhm":"150121198603226428","xxdz":"百度大厦","sqdzbdjd":116.307393,"sqdzbdwd":40.057771,"data":"{\"v\":\"3.4.1\",\"sfzmhm\":\"150121198603226428\",\"s-source\":\"bjjj-android\",\"timestamp\":\"1676214296000\"}"}

The comparison of the data field is very obvious. The first type uses shell variables and directly puts double quotes into the generated json, resulting in a quotation mark matching error; the latter type uses the jq built-in variable, which automatically escapes the double quotes inside the data. Thus conforming to the json syntax.

To sum up, using the jq variable and json template to construct the request will make the generated json string grammatical, the script becomes clear, and the data is easy to maintain. The recommendation index is five stars.

date

A large number of date processing in the script relies on the date command, and one of the more interesting points is the difference between mac date and unix date:

# mac date performs differs with other unix..
if [ ${IS_MAC} -eq 1 ]; then 
    issuedate=$(date "-v+${expire}d" '+%Y-%m-%d')
else 
    issuedate=$(date '+%Y-%m-%d' -d "+${expire} days")
fi

When calculating the date after N days from today, the parameters used by mac date are different from unix date, its form is -v+Nd, and the form of unix date is -d "+N days". For this reason, a function to judge whether the current platform is macOS is added at the beginning of the script: is_macos.

Another thing to note is that the timestamp in the request parameter is accurate to milliseconds, date "+%s"but only to seconds. Here is a tricky way to add 3 zeros to finish the job: date "+%s000".

curl

There is nothing to say about curl as the workhorse of the request, it has always been so reliable. There are two main points to note here.

headers

The request header is stored in the shell array to form a whole string:

local stateheader #=() adb shell not support =() initialize an array..
stateheader[0]="Accept-Language:${lang}"
stateheader[1]="User-Agent:${agent}"
stateheader[2]="source:${source}"
stateheader[3]="authorization:${auth}"
stateheader[4]="Content-Type:${content}"
stateheader[5]="Host:${host}"
stateheader[6]="Connection:Keep-Alive"
stateheader[7]="Accept-Encoding:gzip"
local headers=""
for var in "${stateheader[@]}"; 
do
    headers="${headers} -H ${var}"
done
echo "state headers: ${headers} -H ${length}" 1>&2
local resp=$(curl -s -k ${headers} -H ${length} -d "${statereq}" "${stateurl}")

Later, the concatenated string will be directly placed in the parameter list of curl. In order to prevent the shell from automatically splitting parameters through spaces, the constructed header cannot have spaces, so there are no spaces before Key and Value. This requires Notice.

In addition, because the Content-Length field changes with the request, in order to reuse this header array, it is not included, but becomes an independent request header: -H ${length}.

do not check certificate

Add the -k option to curl, which is critical for some environments accessing the server through a proxy, without this option may cause curl to fail directly:

curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Equivalent to wget's --no-check-certificate option.

layout script

User-related configurations are stored in the config.ini file:

% cat config.ini
# idcard who own the car
userid=150121198603226428
# car number to indicate  which car should I issue permit for,
# especially when you have many cars
vehicle=津ADY1951
# user login tokens, get by network package capture..
authorization=f36abdfa-8878-46bf-91d9-5666f808e9a4
source=8724a2428c3f47358741f978fd082810
# name & idcard who drive the car
# can be same with the car owner
drivername=云海
driverid=150121198603226428

You need to customize your own configuration when using it. The following explains field by field:

  • userid: The ID number corresponding to the account, this is only used for verification, if it does not match the ID card found through the user credential, it will cause the execution of the script to be interrupted to prevent misuse
  • vehicle: License plate number, used to distinguish which one to apply for when there are multiple vehicles under the name. If there is only one, you also need to specify it
  • authorization & source: user credentials, obtained through the VNET packet capture described above
  • drivername & driverid: The information of the driver applying for the Beijing Entry Permit, which can be different from the account, but must be "checked for this person"

The bid date is not in the configuration, but determined according to the nearest principle: if the application time is before 12:00 noon on the same day, apply for the Beijing Entry Permit on the same day; otherwise, apply for the Beijing Entry Permit on the next day. This is convenient for periodic execution.

Timed execution

With the above script and configuration, timing execution is a piece of cake. On the Unix system, use crontab to add timing calls:

> crontab -e
0 1 * * 1 cd /home/users/yunhai01/code/jinjing365; date >> jinjing.log; sh jinjing.sh >> jinjing.log 2>>verbose.log

Executed at 1:00 am every Monday. On Windows, you can also add scheduled tasks to realize timing calls, and the command part can be written as follows:

Batch jinjing.bat will call jinjing.sh directly:

cd /d %~dp0
bash.exe jinjing.sh >> jinjing.log 2>>verbose.log

Among them, %~dp0 indicates the directory where the script is located.

The premise of being able to write this way is that git bash and jq for windows have been installed, and their paths (such as C:\Program Files\Git\bin) are placed in the PATH environment variable.

If you specify the "Use Git and optional Unix tools from the Command Prompt" option when installing git bash, the PATH environment variable can be automatically set by the installation package.

epilogue

In fact, when writing this article, there are still some problems with the script running. It always returns a 500 error (currently there are many people handling business, please try again later). If you directly copy the script in the article, there is a high probability that it will not work. .

In response to this problem, the tracking process is also quite dramatic. Due to space limitations, I plan to record it as a separate article and share it later when I have time.

Therefore, it is best to directly clone the code base ( jinjing365 ), where the latest patches will be included, and push to github as soon as the problem is fixed.

Welcome to submit bug feedback, feature patch, small appreciation, if you think it is not bad.

postscript

In the process of implementing this tool, I referred to  some ideas of the woodheader/jjz  project on github. For example, the use of VNET is obtained from here.

The jjz project has many advantages, and the social software notification function is one of them. When the result is processed, it can be notified as soon as possible to avoid delaying the business.

Because I don't want to install WeChat, Dingding, etc., no similar functions have been added to jinjing365 at present. If you don't have your own server available, it is an easy way to directly borrow the notification group of this project.

One disadvantage of jjz is that it does not support the scene of multiple cars under one person's name, and this is the strength of jinjing365 (an advertisement by the way).

In addition, when encountering a 500 error, I was inspired by asking woodheader, and I would like to express my gratitude here.

Finally, you can also develop automatic application solutions for Beijing entry permits in other languages, such as python, lua, javascript..., there is no shell that can do what they can't do, the choice of which language is mainly based on familiarity and convenience. The reason why this article puts the Beijing Entry Permit application rules and message analysis at the front.

Another point to note is to control the frequency of requests, once a week is enough, and at most no more than once a day. If the server is hung up too often, you may be hit by anti-cheating. If you add verification parameters at least, if you change the interface or seal your account, everyone will suffer.

Finally: If you don’t want to experience the feeling of not being able to find information when learning, no one answering questions, and giving up after persisting for a few days, here I will share with you some learning resources for automated testing, hoping to give you some guidance on the way forward. Come to help, friends can get it for free if they need it 【保证100%免费】

Collection of software testing interview questions

Our advanced study of automated testing must be to find a high-paying job. The following interview questions are the latest interview materials from first-line Internet companies such as Ali, Tencent, and Byte, and some Byte bosses have given authoritative answers. After completing this set of interview materials, I believe everyone can find a satisfactory job.

How to get the video file:

Guess you like

Origin blog.csdn.net/m0_75277660/article/details/130625187