├── .idea
├── .gitignore
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── pymirai.iml
├── AndroidQQ.py
├── LICENSE
├── README.md
├── datas
├── main.py
├── pymirai
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-37.pyc
│ └── __init__.cpython-38.pyc
├── binary
│ └── __init__.py
├── client
│ ├── __init__.py
│ ├── client.py
│ ├── entities.py
│ └── global_.py
├── net
│ ├── __init__.py
│ └── tcp.py
├── protocol
│ └── __init__.py
├── signals.py
└── utils
│ ├── __init__.py
│ ├── __pycache__
│ ├── __init__.cpython-37.pyc
│ ├── __init__.cpython-38.pyc
│ ├── pack.cpython-37.pyc
│ ├── pack.cpython-38.pyc
│ ├── tea.cpython-37.pyc
│ ├── tea.cpython-38.pyc
│ ├── tlv.cpython-37.pyc
│ ├── tools.cpython-37.pyc
│ ├── tools.cpython-38.pyc
│ └── unpack.cpython-37.pyc
│ ├── pack.py
│ ├── tea.py
│ ├── tlv.py
│ ├── tools.py
│ └── unpack.py
└── tests
├── data.txt
├── test_login.py
├── test_pack.py
└── test_signal.py
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 默认忽略的文件
2 | /shelf/
3 | /workspace.xml
4 | # 数据源本地存储已忽略文件
5 | /../../../../:\jhc\pymirai\.idea/dataSources/
6 | /dataSources.local.xml
7 | # 基于编辑器的 HTTP 客户端请求
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/pymirai.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AndroidQQ.py:
--------------------------------------------------------------------------------
1 | from pymirai.utils.pack import Pack
2 | from pymirai.utils.unpack import Unpack
3 | from pymirai.utils.tlv import Tlv
4 | from pymirai.utils.tea import Tea
5 | from pymirai.utils.tools import *
6 | import time
7 | import hashlib
8 |
9 | class AndroidQQ(object):
10 | def __init__(self,qq,password,xy):
11 | self.qq = qq
12 | self.password = password
13 | self.xy = xy #协议
14 | self.starttime = int(time.time())
15 | self.requestId = 10000
16 | self.tgtkey = getRandomBin(16)
17 | self.sharekey = hex2bytes('4A ED 5E CF F6 19 92 A8 BB 62 B3 A8 B3 C4 B0 8E')
18 | self.publickey = hex2bytes('04 5F FB B8 6D 00 A3 7F A9 9B 6A DB 6B C5 B1 75 B3 DD 51 5A FF 66 F6 04 76 85 BA 7F 66 69 69 D8 72 6F 4E 8F 40 B6 EC 17 80 F0 64 A5 51 2F 2B AD 18 5C C2 50 A9 4E BB 25 49 E4 D0 65 54 F9 66 0F A0')
19 | self.privatekey = hex2bytes('00 00 00 21 00 94 C7 25 8B 78 45 33 AB 23 73 B4 3A 60 AB 37 1D D4 53 3B 5A BD FB D6 43 C7 A2 3F CB 5A 08 01 A5')
20 | self.msgCookies = bytes()
21 |
22 | m = hashlib.md5()
23 | m.update(password.encode(encoding='utf-8'))
24 | self.md5pass = m.digest()
25 | m = hashlib.md5()
26 | m.update(self.md5pass + bytes(4) + int2bytes(int(self.qq),4))
27 | self.md52pass = m.digest()
28 |
29 | self.deviceguid = 'C3 6D 03 70 6B 7C 4E DD C0 77 46 91 C1 FB 91 F8'
30 | self.devicename = 'oppo r9 plustm a'
31 | self.devicebrand = 'oppo'
32 | self.deviceMac = '54 44 61 90 FC 9C 7E 08 C4 13 59 26 B8 73 4B C2'
33 | self.deviceImsi = '460001330114682'
34 | self.deviceimie = '865166024867445'
35 | self.ver = '|' + self.deviceImsi + '|A8.4.10.b8c39faf'
36 | self.bssid = ''
37 | self.ssid = 'dlb'
38 | self.AndroidId = 'CC 3C DD 51 8A 92 6C 6C 54 FF 46 48 CE E2 1D 29'
39 | self.appid = 537065990
40 | self.appid2 = 537065990
41 | self.main_signmap = 34869472
42 | self.apk_v = '8.4.10'
43 | self.apk_sig = 'A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D'
44 | self.apkid = 'com.tencent.mobileqq'
45 | self.sdkversion = '6.0.0.2438'
46 |
47 | def Pack_Login(self):
48 | pack = Pack()
49 | pack.write_short(9)
50 | pack.write_short(23) #23个tlv
51 | pack.write_bytes(Tlv.tlv018(self.qq))
52 | pack.write_bytes(Tlv.tlv001(self.qq,self.starttime))
53 | pack.write_bytes(Tlv.tlv106(self.qq,self.md5pass,self.md52pass,self.tgtkey,self.deviceguid,self.starttime,self.appid))
54 | pack.write_bytes(Tlv.tlv116(3))
55 | pack.write_bytes(Tlv.tlv100(self.appid,self.main_signmap))
56 | pack.write_bytes(Tlv.tlv107())
57 | pack.write_bytes(Tlv.tlv142(self.apkid))
58 | pack.write_bytes(Tlv.tlv144(self.tgtkey,Tlv.tlv109(self.AndroidId),Tlv.tlv124(),Tlv.tlv128(self.devicename,self.devicebrand,self.deviceguid),Tlv.tlv16E(self.devicename)))
59 | pack.write_bytes(Tlv.tlv145(self.deviceguid))
60 | pack.write_bytes(Tlv.tlv147(self.apk_v,self.apk_sig))
61 | pack.write_bytes(Tlv.tlv154(self.requestId))
62 | pack.write_bytes(Tlv.tlv141())
63 | pack.write_bytes(Tlv.tlv008())
64 | pack.write_bytes(Tlv.tlv511())
65 | pack.write_bytes(Tlv.tlv187(self.deviceMac))
66 | pack.write_bytes(Tlv.tlv188(self.deviceMac))
67 | pack.write_bytes(Tlv.tlv194(self.deviceImsi))
68 | pack.write_bytes(Tlv.tlv191())
69 | pack.write_bytes(Tlv.tlv202(self.bssid,self.ssid))
70 | pack.write_bytes(Tlv.tlv177(self.starttime,self.sdkversion))
71 | pack.write_bytes(Tlv.tlv516())
72 | pack.write_bytes(Tlv.tlv521())
73 | pack.write_bytes(Tlv.tlv525())
74 |
75 | pkt = Tea.encrypt(pack.get_all(),self.sharekey)
76 | pkt = self.Pack_LoginHead(pkt,0)
77 | pkt = self.Pack_Head(pkt,1)
78 | return pkt
79 |
80 | def Pack_LoginHead(self,pkt,mtype):
81 | pack = Pack()
82 | pack.write_int(self.requestId)
83 | pack.write_int(self.appid)
84 | pack.write_int(self.appid2)
85 | pack.write_hex('01 00 00 00 00 00 00 00 00 00 01 00 00 00 00 04')
86 | pack.write_int(17)
87 | pack.write_str('wtlogin.login')
88 | pack.write_hex('00 00 00 08')
89 | pack.write_bytes(getRandomBin(4))
90 | pack.write_int(len(self.deviceimie)+4)
91 | pack.write_str(self.deviceimie)
92 | if mtype == 2:
93 | pack.write_hex('00 00 00 14')
94 | pack.write_bytes(getRandomBin(16))
95 | else:
96 | pack.write_hex('00 00 00 04')
97 | pack.write_short(len(self.ver)+2)
98 | pack.write_str(self.ver)
99 | if self.xy == 1:
100 | pack.write_hex('00 00 00 04')
101 | else:
102 | pack.write_hex('00 00 00 2A')
103 | pack.write_hex('62 24 31 65 62 63 38 35 64 65 37 33 36 35 64 65 34 64 31 35 35 63 65 34 30 31 31 30 30 30 31 35 38 31 34 37 31 64')
104 |
105 | headpkt = pack.get_all()
106 | pack.set_empty()
107 | pack.write_int(len(headpkt)+4)
108 | pack.write_bytes(headpkt)
109 | headpkt = pack.get_all()
110 |
111 | pack.set_empty()
112 | pack.write_hex('1F 41 08 10 00 01')
113 | pack.write_int(int(self.qq))
114 | if self.xy == 1:
115 | pack.write_hex('03 87 00 00 00 00 02 00 00 00 00 00 00 00 00 01 01')
116 | elif mtype == 0:
117 | pack.write_hex('03 87 00 00 00 00 02 00 00 00 00 00 00 00 00 02 01')
118 | else:
119 | pack.write_hex('03 07 00 00 00 00 02 00 00 00 00 00 00 00 00 02 01')
120 | pack.write_bytes(getRandomBin(16))
121 | if self.xy == 1:
122 | pack.write_hex('01 02')
123 | elif mtype == 2:
124 | pack.write_hex('01 31 00 02')
125 | else:
126 | pack.write_hex('01 31 00 01')
127 | pack.write_short(len(self.publickey))
128 | pack.write_bytes(self.publickey)
129 | pack.write_bytes(pkt)
130 |
131 | pkt = pack.get_all()
132 |
133 | pack.set_empty()
134 | pack.write_hex('02')
135 | pack.write_short(len(pkt)+4)
136 | pack.write_bytes(pkt)
137 | pack.write_hex('03')
138 |
139 | pkt = pack.get_all()
140 |
141 | pack.set_empty()
142 | pack.write_bytes(headpkt)
143 | pack.write_int(len(pkt)+4)
144 | pack.write_bytes(pkt)
145 |
146 | pkt = Tea.encrypt(pack.get_all(),bytes(16))
147 | return pkt
148 |
149 | def Pack_Head(self,pkt,mtype):
150 | pack = Pack()
151 | if mtype == 1:
152 | pack.write_hex('00 00 00 0A 02 00 00 00 04')
153 | elif mtype == 2:
154 | pass
155 | elif mtype == 3:
156 | pack.write_hex('00 00 00 0B 01')
157 | pack.write_int(self.requestId)
158 | else:
159 | pack.write_hex('00 00 00 0B 02')
160 | pack.write_int(self.requestId)
161 | pack.write_hex('00 00 00')
162 | pack.write_short(len(self.qq)+4)
163 | pack.write_str(self.qq)
164 | pack.write_bytes(pkt)
165 |
166 | pkt = pack.get_all()
167 |
168 | pack.set_empty()
169 | pack.write_int(len(pkt)+4)
170 | pack.write_bytes(pkt)
171 |
172 | pkt = pack.get_all()
173 | return pkt
174 |
175 | def Unpack_Login(self,pkt:bytes):
176 | position = pkt.find(str2bytes(self.qq))
177 | pkt = pkt[position+len(self.qq):]
178 | pkt = Tea.decrypt(pkt,bytes(16))
179 | up = Unpack(pkt)
180 | headlen = up.getInt()
181 | headdata = up.getBin(headlen - 4)
182 | maindata = up.getAll()
183 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The mirai implemention on python
2 |
3 | ## 在大佬的帮助下凑齐了工具类,总算可以对付字节流了
4 | - ~~py的字节流处理啊……简直像隔着裤子撸管~~
5 |
6 |
7 | - [x] 下一步解包登录
8 |
--------------------------------------------------------------------------------
/datas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/datas
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from socket import socket,AF_INET,SOCK_STREAM
2 | from pymirai.utils.tools import *
3 | from pymirai.utils.pack import Pack
4 | from pymirai.utils.tea import Tea
5 | import time
6 | from AndroidQQ import AndroidQQ
7 | #print(bytes2hex(int2bytes(2193096276,4)))
8 | Loginqq = AndroidQQ('你的名字','你的密码',0)
9 | #print(bytes2hex(Loginqq.Pack_Login()))
10 | s=socket(AF_INET,SOCK_STREAM)
11 | s.connect(("113.96.12.224",8080))
12 | s.send(Loginqq.Pack_Login())
13 | buf = s.recv(2048)
14 | s.close()
15 | #print(bytes2hex(buf))
16 | Loginqq.Unpack_Login(buf)
17 |
18 | # qq='2193096276'
19 | # pack = Pack()
20 | # pack.setShort(len(qq))
21 | # pack.setStr(qq)
22 | # pack.setHex('00 04')
23 | # pack.setInt(int(qq))
24 | # print(Str2Bytes('哈哈哈'))
25 | # print(Bytes2Hex(pack.getAll()))
26 |
27 | # a = Bytes2Hex(tea.encrypt(Str2Bytes('哈哈哈'),Hex2Bytes('12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12')))
28 | # print(a)
29 | # b = Bytes2Hex(tea.decrypt(Hex2Bytes(a),Hex2Bytes('12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12')))
30 | # print(b)
31 |
--------------------------------------------------------------------------------
/pymirai/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | def main():
4 | pass
5 |
6 |
7 | if __name__ == "__main__":
8 | main()
9 |
--------------------------------------------------------------------------------
/pymirai/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/pymirai/binary/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | jce 序列化相关
4 | """
5 |
6 |
7 | def main():
8 | pass
9 |
10 |
11 | if __name__ == "__main__":
12 | main()
13 |
--------------------------------------------------------------------------------
/pymirai/client/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | def main():
4 | pass
5 |
6 |
7 | if __name__ == "__main__":
8 | main()
9 |
--------------------------------------------------------------------------------
/pymirai/client/client.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | from https://github.com/Mrs4s/MiraiGo/blob/94779a6b765bf1acd23bd99fa1cbf1b2ff6fa75a/client/client.go
4 | """
5 | from typing import List
6 | import ipaddress
7 |
8 | from pydantic import BaseModel
9 |
10 | from pymirai.client.entities import FriendInfo, GroupInfo
11 | from pymirai.client.global_ import VersionInfo
12 | from pymirai.net import TCPConnection
13 | from pymirai.signals import SignalManager
14 |
15 |
16 | class QQClient(BaseModel):
17 | """
18 | qq客户端 sms是短信验证码
19 | """
20 | uin: int
21 | password_md5: str
22 | allow_slider: bool
23 |
24 | nickname: str
25 | age: int
26 | gender: int
27 | friend_list: List[FriendInfo]
28 | group_list: List[GroupInfo]
29 | online: bool
30 | net_looping: bool
31 |
32 | sequence_id: int
33 | outgoing_packet_session_id: bytes
34 | random_key: bytes
35 | conn: TCPConnection # tcp连接?
36 | connect_time: int
37 |
38 | handlers: SignalManager # 这里跟go不一样 ,用signal订阅发布模式在收到数据后广播信号 ,函数来处理
39 | servers: List[ipaddress.IPv4Address]
40 | curr_server_index: int
41 | retry_times: int
42 | version: VersionInfo
43 |
44 | sync_cookie: bytes
45 | pub_account_cookie: bytes
46 | msg_ctrl_buf: bytes
47 | ksid: bytes # 登录包
48 | t104: bytes
49 | t174: bytes
50 | t402: bytes
51 | t150: bytes
52 | t149: bytes
53 | t528: bytes
54 | t530: bytes
55 | rollback_sig: bytes
56 | time_diff: int
57 | sig_info: "LoginSigInfo" # TODO 抄go的
58 | pwd_flag: bool
59 | last_message_seq: int
60 |
61 |
62 | class LoginSigInfo(BaseModel):
63 | pass
64 |
--------------------------------------------------------------------------------
/pymirai/client/entities.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | from https://github.com/Mrs4s/MiraiGo/blob/94779a6b765bf1acd23bd99fa1cbf1b2ff6fa75a/client/entities.go#L43
4 | """
5 | import asyncio
6 | from typing import List
7 |
8 | from pydantic import BaseModel
9 |
10 | from .client import QQClient
11 |
12 | NeedCaptcha = 1
13 | OtherLoginError = 3
14 | UnsafeDeviceError = 4
15 | SMSNeededError = 5
16 | TooManySMSRequestError = 6
17 | SMSOrVerifyNeededError = 7
18 | SliderNeededError = 8
19 | UnknownLoginError = -1
20 |
21 | Owner = 0
22 | Administrator = 1
23 | Member = 2
24 |
25 | AndroidPhone = 1
26 | IPad = 2
27 | AndroidWatch = 3
28 | MacOS = 4
29 |
30 |
31 | class LoginResponse(BaseModel):
32 | success: bool
33 | error: int
34 | # Captcha info
35 | captcha_image: bytes
36 | captcha_sign: bytes
37 | # Unsafe device
38 | verify_url: str
39 | # SMS needed
40 | sms_phone: str
41 | # other error
42 | error_message: str
43 |
44 |
45 | class FriendInfo(BaseModel):
46 | uin: int
47 | nike_name: str
48 | remark: str
49 | face_id: int
50 |
51 |
52 | class FriendListResponse(BaseModel):
53 | total_count: int
54 | list_: List[FriendInfo]
55 |
56 |
57 | class SummaryCardInfo(BaseModel):
58 | uin: int
59 | sex: bytes
60 | age: int
61 | nick_name: str
62 | level: int
63 | city: str
64 | sign: str
65 | mobile: str
66 | login_days: int
67 |
68 |
69 | class GroupInfo(BaseModel):
70 | uin: int
71 | code: int
72 | name: str
73 | memo: str
74 | owner_uin: int
75 | member_count: int
76 | max_member_count: int
77 | members: List["GroupMemberInfo"]
78 | client: QQClient
79 | last_msg_seq: int
80 | lock: asyncio.Lock # 我们为什么要锁?我们是高贵的py,有gil的 昵昵说,消息异步处理,可能修改members,因此要加读写锁.那就加上吧
81 |
82 |
83 | class GroupMemberInfo(BaseModel):
84 | group: GroupInfo
85 | uin: int
86 | gender: bytes
87 | nick_name: str
88 | card_name: str
89 | level: int
90 | join_time: int
91 | last_speak_time: int
92 | special_title: str
93 | special_title_expire_time: int # 震惊:特殊头衔居然会过期
94 | permission: int # go里面是MemberPermission int emmm 要不变枚举?
95 |
96 |
97 | class GroupMuteEvent(BaseModel):
98 | group_code: int
99 | operator_uin: int
100 | target_uin: int
101 | time: int
102 |
103 |
104 | class GroupMessageRecalledEvent(BaseModel):
105 | group_code: int
106 | operator_uin: int
107 | author_uin: int
108 | message_id: int
109 | time: int
110 |
111 |
112 | class FriendMessageRecalledEvent(BaseModel):
113 | friend_uin: int
114 | message_id: int
115 | time: int
116 |
117 |
118 | class GroupLeaveEvent(BaseModel):
119 | group: GroupInfo
120 | operator: GroupMemberInfo
121 |
122 |
123 | class MemberJoinGroupEvent(BaseModel):
124 | group: GroupInfo
125 | member: GroupMemberInfo
126 |
127 |
128 | class MemberCardUpdatedEvent(BaseModel):
129 | group: GroupInfo
130 | old_card: str
131 | member: GroupMemberInfo
132 |
133 |
134 | # TODO 这里有个接口 暂时作用不明确
135 |
136 | class MemberLeaveGroupEvent(BaseModel):
137 | group: GroupInfo
138 | member: GroupMemberInfo
139 | operator: GroupMemberInfo
140 |
141 |
142 | class MemberPermissionChangedEvent(BaseModel):
143 | group: GroupInfo
144 | member: GroupMemberInfo
145 | old_permission: int
146 | new_permission: int
147 |
148 |
149 | class ClientDisconnectedEvent(BaseModel):
150 | message: str
151 |
152 |
153 | class NewFriendRequest(BaseModel):
154 | request_id: int
155 | message: str
156 | requester_uin: int
157 | requester_nick: int
158 | client: QQClient
159 |
160 |
161 | class LogEvent(BaseModel):
162 | type_: str
163 | message: str
164 |
165 |
166 | class ServerUpdatedEvent(BaseModel):
167 | """
168 | 服务器ip换了 TODO 抄这个得看pb了先放放
169 | """
170 | pass
171 |
172 |
173 | class NewFriendEvent(BaseModel):
174 | friend: FriendInfo
175 |
176 |
177 | class OfflineFileEvent(BaseModel):
178 | file_name: str
179 | file_size: int
180 | sender: int
181 | download_url: str
182 |
183 |
184 | class OcrResponse(BaseModel):
185 | texts: List["TextDetection"] # TODO 看go的ocr实现
186 | language: str
187 |
188 |
189 | class TextDetection(BaseModel):
190 | text: str
191 | confidence: int
192 | coordinates: List["Coordinate"] # 坐标
193 |
194 |
195 | class Coordinate(BaseModel):
196 | x: int
197 | y: int
198 |
199 |
200 | class GroupMemberListResponse(BaseModel):
201 | next_uin: int
202 | list_: List[GroupMemberInfo]
203 |
204 |
205 | class ImageUploadResponse(BaseModel):
206 | pass
207 |
208 | # TODO 抄到206了 不写了
209 |
--------------------------------------------------------------------------------
/pymirai/client/global_.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | from /client/global.go
4 | """
5 | from pydantic import BaseModel
6 |
7 |
8 | class DeviceInfo(BaseModel):
9 | display: bytes
10 | product: bytes
11 | device: bytes
12 | board: bytes
13 | brand: bytes
14 | model: bytes
15 | boot_loader: bytes
16 | finger_print: bytes
17 | boot_id: bytes
18 | proc_version: bytes
19 | base_band: bytes
20 | sim_info: bytes
21 | os_type: bytes
22 | mac_address: bytes
23 | ipaddress: bytes
24 | wifi_bssid: bytes
25 | wifi_ssid: bytes
26 | imsi_md5: bytes
27 | imei: str
28 | android_id: bytes
29 | apn: bytes
30 | guid: bytes
31 | tgtgt_key: bytes
32 | protocol: int
33 | version: "Version"
34 |
35 |
36 | class Version(BaseModel):
37 | incremental: bytes
38 | release: bytes
39 | code_name: bytes
40 | sdk: int
41 |
42 |
43 | class DeviceInfoFile(BaseModel):
44 | display: str
45 | product: str
46 | device: str
47 | board: str
48 | model: str
49 | fingerprint: str
50 | boot_id: str
51 | proc_version: str
52 | protocol: int # 0: Pad 1: Phone 2: Watch 3: Mac
53 | imei: str
54 |
55 |
56 | class GroupMessageBuilder(BaseModel):
57 | """
58 | TODO 留给pb
59 | """
60 |
61 |
62 | class VersionInfo(BaseModel):
63 | apk_sign: bytes
64 | apk_id: str
65 | sort_version_name: str
66 | sdk_version: str
67 | app_id: int
68 | build_time: int
69 | sso_version: int
70 | misc_bitmap: int
71 | sub_sigmap: int
72 | main_sigmap: int
73 |
74 |
--------------------------------------------------------------------------------
/pymirai/net/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from .tcp import TCPConnection
3 |
--------------------------------------------------------------------------------
/pymirai/net/tcp.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import asyncio
3 | import socket
4 |
5 | CHUNK_SIZE = -1
6 |
7 |
8 | class TCPConnection:
9 | """
10 | 对流的封装 方便处理
11 | """
12 |
13 | def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
14 | self.buffer = b""
15 | self.closed = False
16 | self.reader = reader
17 | self.writer = writer
18 | self.socket: socket.socket = writer.get_extra_info("socket")
19 |
20 | def __str__(self):
21 | return f"TCPConnection to {self.peername}"
22 |
23 | @classmethod
24 | async def new(cls, host: str, port: int, **kw) -> "TCPConnection":
25 | reader, writer = await asyncio.open_connection(host, port, **kw)
26 | return cls(reader, writer)
27 |
28 | @property
29 | def peername(self):
30 | return self.writer.get_extra_info('peername')
31 |
32 | def add_into_buffer(self, data: bytes) -> None:
33 | self.buffer += data
34 |
35 | def recv(self, size: int = CHUNK_SIZE) -> bytes:
36 | return self.socket.recv(size)
37 |
38 | def send(self, data: bytes) -> int:
39 | return self.socket.send(data)
40 |
41 | async def read(self, size: int = CHUNK_SIZE) -> bytes:
42 | return await self.reader.read(size)
43 |
44 | def write(self, data: bytes) -> None:
45 | self.writer.write(data)
46 |
47 | async def drain(self) -> None:
48 | await self.writer.drain()
49 |
50 | async def flush(self):
51 | self.write(self.buffer)
52 | self.buffer = b""
53 | await self.drain()
54 |
55 | async def close(self):
56 | self.writer.close()
57 | await self.writer.wait_closed()
58 | self.closed = True
59 |
--------------------------------------------------------------------------------
/pymirai/protocol/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | 组包的工具
4 | """
5 |
6 |
7 | def main():
8 | pass
9 |
10 |
11 | if __name__ == "__main__":
12 | main()
13 |
--------------------------------------------------------------------------------
/pymirai/signals.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | 接收到数据的时候广播信号,调用函数来处理 对,我抄了scrapy
4 | """
5 | import asyncio
6 | from typing import Callable, Any, List, Tuple
7 |
8 | from pydispatch import dispatcher
9 |
10 |
11 | class SignalManager:
12 | def __init__(self, sender=dispatcher.Anonymous):
13 | self.sender = sender
14 |
15 | def connect(self, receiver: Callable, signal: Any, **kwargs) -> None:
16 | """
17 | 注册一个信号的响应函数
18 | :param receiver: 响应函数
19 | :param signal: 信号对象
20 | :param kwargs:
21 | :return:
22 | """
23 | return dispatcher.connect(receiver, signal, self.sender, **kwargs)
24 |
25 | def disconnect(self, receiver: Callable, signal, **kwargs) -> None:
26 | """
27 | 断开一个接收器的对于给定信号的连接
28 | :param receiver:
29 | :param signal:
30 | :param kwargs:
31 | :return:
32 | """
33 | return dispatcher.disconnect(receiver, signal, self.sender, **kwargs)
34 |
35 | def disconnect_all(self, signal, **kwargs) -> None:
36 | """
37 | 将所有接收器断开给定信号的连接
38 | :param signal:
39 | :param kwargs:
40 | :return:
41 | """
42 | for receiver in dispatcher.liveReceivers(dispatcher.getAllReceivers(self.send, signal)):
43 | dispatcher.disconnect(receiver, signal, self.sender, **kwargs)
44 |
45 | def send(self, signal, *args, **kwargs) -> List[Tuple]:
46 | """
47 |
48 | :param signal:
49 | :param kwargs:
50 | :return: list of tuple pairs [(receiver, response), ... ]
51 | """
52 | kwargs.setdefault('sender', self.sender)
53 | return dispatcher.send(signal, self.sender, *args, **kwargs)
54 |
55 | async def send_async(self, signal, *args, **kwargs) -> List[Tuple]:
56 | """
57 | receiver都是协程的情况下使用
58 | :param signal:
59 | :param kwargs:
60 | :return: list of tuple pairs [(receiver, response), ... ]
61 | """
62 |
63 | async def _process_data(data_: List[Tuple]):
64 | ret = await asyncio.gather(*map(lambda x: x[1], data_))
65 | return list(zip(map(lambda x: x[0], data_), ret))
66 |
67 | data = dispatcher.send(signal, self.sender, *args, **kwargs)
68 | return await _process_data(data)
69 |
--------------------------------------------------------------------------------
/pymirai/utils/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | def main():
4 | pass
5 |
6 |
7 | if __name__ == "__main__":
8 | main()
9 |
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/__init__.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/__init__.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/pack.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/pack.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/pack.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/pack.cpython-38.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/tea.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/tea.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/tea.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/tea.cpython-38.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/tlv.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/tlv.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/tools.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/tools.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/tools.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/tools.cpython-38.pyc
--------------------------------------------------------------------------------
/pymirai/utils/__pycache__/unpack.cpython-37.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synodriver/pymirai/2f455db584da342ebd1a113e78deb89b43f5df22/pymirai/utils/__pycache__/unpack.cpython-37.pyc
--------------------------------------------------------------------------------
/pymirai/utils/pack.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | python的二进制操作太挫了 需要加以改进
4 | """
5 |
6 |
7 | class Pack(object):
8 | """
9 | 组包
10 | """
11 |
12 | def __init__(self):
13 | self.buffer = bytes()
14 |
15 | def set_empty(self) -> None:
16 | """
17 | 清空
18 | :return:
19 | """
20 | self.buffer = bytes()
21 |
22 | def get_all(self) -> bytes:
23 | """
24 | 返回全部流
25 | :return:
26 | """
27 | return self.buffer
28 |
29 | def write_hex(self, hexstr: str) -> None:
30 | str_bytes: str = hexstr.strip()
31 | pkt = bytes.fromhex(str_bytes)
32 | self.buffer += pkt
33 |
34 | def write_int(self, num: int) -> None:
35 | pkt = num.to_bytes(length=4, byteorder='big')
36 | self.buffer += pkt
37 |
38 | def write_short(self, num) -> None:
39 | pkt = int(num).to_bytes(length=2, byteorder='big') # , signed=True)
40 | self.buffer += pkt
41 |
42 | def write_str(self, text: str, encoding:str="utf-8") -> None:
43 | pkt = text.encode(encoding)
44 | self.buffer += pkt
45 |
46 | def write_bytes(self, byte: bytes) -> None:
47 | self.buffer += byte
48 |
49 | def write_qq(self, qq: int) -> None:
50 | """
51 | 编码qq 12345 -> 31 32 33 34 35
52 | :param qq:
53 | :return:
54 | """
55 | _qq = map(int, str(qq))
56 | pkt: bytes = b"".join(bytes([48 | i]) for i in _qq)
57 | self.buffer += pkt
58 |
59 | def set_long_token(self, bin_) -> None:
60 | self.write_int(len(bin_))
61 | self.write_bytes(bin_)
62 |
63 | def set_token(self, bin_) -> None:
64 | self.write_short(len(bin_))
65 | self.write_bytes(bin_)
66 |
--------------------------------------------------------------------------------
/pymirai/utils/tea.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import struct
3 |
4 |
5 | def xor(a, b):
6 | op = 0xffffffff
7 | a1, a2 = struct.unpack(b'>LL', a[0:8])
8 | b1, b2 = struct.unpack(b'>LL', b[0:8])
9 | return struct.pack(b'>LL', (a1 ^ b1) & op, (a2 ^ b2) & op)
10 |
11 |
12 | def tea_code(v, k):
13 | n = 16
14 | op = 0xffffffff
15 | delta = 0x9e3779b9
16 | k = struct.unpack(b'>LLLL', k[0:16])
17 | y, z = struct.unpack(b'>LL', v[0:8])
18 | s = 0
19 | for i in range(n):
20 | s += delta
21 | y += (op & (z << 4)) + k[0] ^ z + s ^ (op & (z >> 5)) + k[1]
22 | y &= op
23 | z += (op & (y << 4)) + k[2] ^ y + s ^ (op & (y >> 5)) + k[3]
24 | z &= op
25 | r = struct.pack(b'>LL', y, z)
26 | return r
27 |
28 |
29 | def tea_decipher(v, k):
30 | n = 16
31 | op = 0xffffffff
32 | y, z = struct.unpack(b'>LL', v[0:8])
33 | a, b, c, d = struct.unpack(b'>LLLL', k[0:16])
34 | delta = 0x9E3779B9
35 | s = (delta << 4) & op
36 | for i in range(n):
37 | z -= ((y << 4) + c) ^ (y + s) ^ ((y >> 5) + d)
38 | z &= op
39 | y -= ((z << 4) + a) ^ (z + s) ^ ((z >> 5) + b)
40 | y &= op
41 | s -= delta
42 | s &= op
43 | return struct.pack(b'>LL', y, z)
44 |
45 |
46 | class Tea:
47 | """QQ TEA 加解密, 64比特明码, 128比特密钥
48 | 这是一个确认线程安全的独立加密模块,使用时必须要有一个全局变量secret_key,要求大于等于16位
49 | """
50 |
51 | @staticmethod
52 | def encrypt(v: bytes, secret_key: bytes):
53 | END_CHAR = b'\0'
54 | FILL_N_OR = 0xF8
55 | vl = len(v)
56 | filln = (8 - (vl + 2)) % 8 + 2
57 | fills = b''
58 | for i in range(filln):
59 | fills = fills + bytes([220])
60 | v = (bytes([(filln - 2) | FILL_N_OR])
61 | + fills
62 | + v
63 | + END_CHAR * 7)
64 | tr = b'\0' * 8
65 | to = b'\0' * 8
66 | r = b''
67 | o = b'\0' * 8
68 | for i in range(0, len(v), 8):
69 | o = xor(v[i:i + 8], tr)
70 | tr = xor(tea_code(o, secret_key), to)
71 | to = o
72 | r += tr
73 | return r
74 |
75 | @staticmethod
76 | def decrypt(v: bytes, secret_key: bytes):
77 | l = len(v)
78 | prePlain = tea_decipher(v, secret_key)
79 | pos = (prePlain[0] & 0x07) + 2
80 | r = prePlain
81 | preCrypt = v[0:8]
82 | for i in range(8, l, 8):
83 | x = xor(tea_decipher(xor(v[i:i + 8], prePlain), secret_key), preCrypt)
84 | prePlain = xor(x, preCrypt)
85 | preCrypt = v[i:i + 8]
86 | r += x
87 | if r[-7:] != b'\0' * 7:
88 | return None
89 | return r[pos + 1:-7]
90 |
91 | # if __name__ == '__main__':
92 | # global secret_key
93 | # secret_key = hex2bytes('11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11')
94 | # print(bytes2hex(secret_key))
95 |
96 | # QQ = TEA()
97 |
98 | # plaintext = '哈哈哈hhh,'
99 | # plaintext = bytes(plaintext,encoding = "utf-8")
100 | # enc = QQ.encrypt(plaintext)
101 | # print(bytes2hex(enc))
102 | # dec = QQ.decrypt(enc)
103 | # print(bytes2hex(dec))
104 |
--------------------------------------------------------------------------------
/pymirai/utils/tlv.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from .pack import Pack
3 | from .tools import getRandomBin,str2bytes
4 | from .tea import Tea
5 |
6 | class Tlv(object):
7 | """
8 | 工具类
9 | """
10 | def __init__(self):
11 | pass
12 |
13 | @staticmethod
14 | def tlv_pack(cmd, bin_):
15 | pack = Pack()
16 | pack.write_hex(cmd)
17 | pack.write_short(len(bin_))
18 | pack.write_bytes(bin_)
19 | return pack.get_all()
20 |
21 | @staticmethod
22 | def tlv018(qq: str):
23 | pack = Pack()
24 | pack.write_hex('00 01 00 00 06 00 00 00 00 10 00 00 00 00')
25 | pack.write_int(int(qq))
26 | pack.write_bytes(bytes(4))
27 | return Tlv.tlv_pack('00 18', pack.get_all())
28 |
29 | @staticmethod
30 | def tlv001(qq,time):
31 | pack = Pack()
32 | pack.write_hex('00 01')
33 | pack.write_bytes(getRandomBin(4))
34 | pack.write_int(int(qq))
35 | pack.write_int(time)
36 | pack.write_bytes(bytes(6))
37 | return Tlv.tlv_pack('00 01',pack.get_all())
38 |
39 | @staticmethod
40 | def tlv106(qq:str,md5pass,md52pass,tgtkey,deviceguid:str,time,appid):
41 | pack = Pack()
42 | pack.write_hex('00 04')
43 | pack.write_bytes(getRandomBin(4))
44 | pack.write_hex('00 00 00 0D')
45 | pack.write_hex('00 00 00 10')
46 | pack.write_hex('00 00 00 00')
47 | pack.write_hex('00 00 00 00')
48 | pack.write_int(int(qq))
49 | pack.write_int(time)
50 | pack.write_hex('00 00 00 00 01')
51 | pack.write_bytes(md5pass)
52 | pack.write_bytes(tgtkey)
53 | pack.write_hex('00 00 00 00 01')
54 | pack.write_hex(deviceguid)
55 | pack.write_int(appid)
56 | pack.write_hex('00 00 00 01')
57 | pack.write_short(len(qq))
58 | pack.write_str(qq)
59 | pack.write_hex('00 00')
60 | return Tlv.tlv_pack('01 06',Tea.encrypt(pack.get_all(),md52pass))
61 |
62 | @staticmethod
63 | def tlv116(mtype):
64 | pack = Pack()
65 | if mtype == 1:
66 | pack.write_hex('00 08 F7 FF 7C 00 01 04 00 01 5F 5E 10 E2')
67 | elif mtype == 2:
68 | pack.write_hex('00 08 F7 FF 7C 00 01 04 00 01')
69 | else:
70 | pack.write_hex('00 0A F7 FF 7C 00 01 04 00 01 5F 5E 10 E2')
71 | return Tlv.tlv_pack('01 16',pack.get_all())
72 |
73 | @staticmethod
74 | def tlv100(appid,main_signmap):
75 | pack = Pack()
76 | pack.write_hex('00 01 00 00 00 0D 00 00 00 10')
77 | pack.write_int(appid)
78 | pack.write_bytes(bytes(4))
79 | pack.write_int(main_signmap)
80 | return Tlv.tlv_pack('01 00',pack.get_all())
81 |
82 | @staticmethod
83 | def tlv107():
84 | pack = Pack()
85 | pack.write_hex('00 00 00 00 00 01')
86 | return Tlv.tlv_pack('01 07',pack.get_all())
87 |
88 | @staticmethod
89 | def tlv142(apkid:str):
90 | pack = Pack()
91 | pack.write_int(len(apkid))
92 | pack.write_str(apkid)
93 | return Tlv.tlv_pack('01 42',pack.get_all())
94 |
95 | @staticmethod
96 | def tlv109(AndroidId):
97 | pack = Pack()
98 | pack.write_hex(AndroidId)
99 | return Tlv.tlv_pack('01 09',pack.get_all())
100 |
101 | @staticmethod
102 | def tlv124():
103 | pack = Pack()
104 | pack.write_hex('00 07')
105 | pack.write_hex('61 6E 64 72 6F 69 64') #android
106 | pack.write_hex('00 05')
107 | pack.write_hex('35 2E 31 2E 31') #5.1.1
108 | pack.write_hex('00 02')
109 | pack.write_hex('00 10')
110 | pack.write_hex('43 68 69 6E 61 20 4D 6F 62 69 6C 65 20 47 53 4D') #China Mobile GSM
111 | pack.write_hex('00 00 00 04')
112 | pack.write_hex('77 69 66 69') #wifi
113 | return Tlv.tlv_pack('01 24',pack.get_all())
114 |
115 | @staticmethod
116 | def tlv128(devicename,devicebrand,deviceguid):
117 | pack = Pack()
118 | pack.write_hex('00 00 01 01 00 11 00 00 00')
119 | pack.write_short(len(devicename))
120 | pack.write_str(devicename)
121 | pack.write_hex('00 10')
122 | pack.write_hex(deviceguid)
123 | pack.write_short(len(devicebrand))
124 | pack.write_str(devicebrand)
125 | return Tlv.tlv_pack('01 28',pack.get_all())
126 |
127 | @staticmethod
128 | def tlv16E(devicename):
129 | pack = Pack()
130 | pack.write_str(devicename)
131 | return Tlv.tlv_pack('01 6E',pack.get_all())
132 |
133 | @staticmethod
134 | def tlv144(tgtkey,tlv109,tlv124,tlv128,tlv16E):
135 | pack = Pack()
136 | pack.write_short(4)
137 | pack.write_bytes(tlv109)
138 | pack.write_bytes(tlv124)
139 | pack.write_bytes(tlv128)
140 | pack.write_bytes(tlv16E)
141 | return Tlv.tlv_pack('01 44',Tea.encrypt(pack.get_all(),tgtkey))
142 |
143 | @staticmethod
144 | def tlv145(deviceguid):
145 | pack = Pack()
146 | pack.write_hex(deviceguid)
147 | return Tlv.tlv_pack('01 45',pack.get_all())
148 |
149 | @staticmethod
150 | def tlv147(apk_v:str,apk_sig:str):
151 | pack = Pack()
152 | pack.write_hex('00 00 00 10')
153 | pack.write_short(len(apk_v))
154 | pack.write_str(apk_v)
155 | pack.write_hex('00 10')
156 | pack.write_hex(apk_sig)
157 | return Tlv.tlv_pack('01 47',pack.get_all())
158 |
159 | @staticmethod
160 | def tlv154(reqid):
161 | pack = Pack()
162 | pack.write_int(reqid)
163 | return Tlv.tlv_pack('01 54',pack.get_all())
164 |
165 | @staticmethod
166 | def tlv141():
167 | pack = Pack()
168 | pack.write_hex('00 01')
169 | pack.write_hex('00 10')
170 | pack.write_hex('43 68 69 6E 61 20 4D 6F 62 69 6C 65 20 47 53 4D')
171 | pack.write_hex('00 02')
172 | pack.write_hex('00 04')
173 | pack.write_hex('77 69 66 69')
174 | return Tlv.tlv_pack('01 41',pack.get_all())
175 |
176 | @staticmethod
177 | def tlv008():
178 | pack = Pack()
179 | pack.write_hex('00 00 00 00 08 04 00 00')
180 | return Tlv.tlv_pack('00 08',pack.get_all())
181 |
182 | @staticmethod
183 | def tlv511():
184 | pack = Pack()
185 | domainlist = ['tenpay.com','qzone.qq.com','vip.qq.com','qun.qq.com','yundong.qq.com']
186 | pack.write_short(len(domainlist))
187 | for x in domainlist:
188 | pack.write_hex('01')
189 | pack.set_token(str2bytes(x))
190 | return Tlv.tlv_pack('05 11',pack.get_all())
191 |
192 | @staticmethod
193 | def tlv187(devicemac:str):
194 | pack = Pack()
195 | pack.write_hex(devicemac)
196 | return Tlv.tlv_pack('01 87',pack.get_all())
197 |
198 | @staticmethod
199 | def tlv188(AndroidId:str):
200 | pack = Pack()
201 | pack.write_hex(AndroidId)
202 | return Tlv.tlv_pack('01 88',pack.get_all())
203 |
204 | @staticmethod
205 | def tlv194(imsi:str):
206 | pack = Pack()
207 | # pack.write_hex(imsi)
208 | pack.write_hex('DE 99 6F 72 08 45 79 04 DE B5 AF 92 27 8E 40 A2')
209 | return Tlv.tlv_pack('01 94',pack.get_all())
210 |
211 | @staticmethod
212 | def tlv191():
213 | pack = Pack()
214 | pack.write_hex('82') #02
215 | return Tlv.tlv_pack('01 91',pack.get_all())
216 |
217 | @staticmethod
218 | def tlv202(bssid,ssid):
219 | pack = Pack()
220 | pack.write_hex('00 10')
221 | pack.write_hex('41 D8 57 AF BD 54 DC 49 DB 42 14 44 7D 09 5D 13')
222 | pack.set_token(str2bytes('"' + ssid + '"'))
223 | return Tlv.tlv_pack('02 02',pack.get_all())
224 |
225 | @staticmethod
226 | def tlv177(time,sdkversion):
227 | pack = Pack()
228 | pack.write_hex('01')
229 | pack.write_int(time)
230 | pack.write_short(len(sdkversion))
231 | pack.write_str(sdkversion)
232 | return Tlv.tlv_pack('01 77',pack.get_all())
233 |
234 | @staticmethod
235 | def tlv516():
236 | pack = Pack()
237 | pack.write_bytes(bytes(4))
238 | return Tlv.tlv_pack('05 16',pack.get_all())
239 |
240 | @staticmethod
241 | def tlv521():
242 | pack = Pack()
243 | pack.write_bytes(bytes(6))
244 | return Tlv.tlv_pack('05 21',pack.get_all())
245 |
246 | @staticmethod
247 | def tlv525():
248 | pack = Pack()
249 | pack.write_hex('00 01 05 36 00 02 01 00')
250 | return Tlv.tlv_pack('05 25',pack.get_all())
--------------------------------------------------------------------------------
/pymirai/utils/tools.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | 操作字节流的
4 | """
5 | import struct
6 | import random
7 |
8 | def int2bytes(num: int, outlen: int) -> bytes:
9 | return num.to_bytes(length=outlen, byteorder='big')
10 |
11 | def bytes2int(bin_) -> int:
12 | return int.from_bytes(bin_,byteorder='big')
13 |
14 | def hex2bytes(hexstr: str) -> bytes:
15 | str_bytes = hexstr.strip().replace("\n", "")
16 | pkt = bytes.fromhex(str_bytes)
17 | return pkt
18 |
19 |
20 | def bytes2hex(bin_: bytes) -> str:
21 | return ''.join(['%02X ' % b for b in bin_])
22 |
23 |
24 | def _bytes2hex(bin_: bytes) -> str:
25 | return bin_.hex().upper()
26 |
27 |
28 | def str2bytes(text: str):
29 | return text.encode('utf-8')
30 |
31 |
32 | def str2hex(text: str):
33 | strBytes = text.encode('utf-8')
34 | return bytes2hex(strBytes)
35 |
36 |
37 | def hex2str(hexstr: str):
38 | strBytes = hexstr.split()
39 | pkt = bytearray(int(x, 16) for x in strBytes)
40 | return pkt.decode('utf-8')
41 |
42 | def getRandomBin(num):
43 | intlist = [random.randint(0,255) for i in range(num)]
44 | pkt = bytearray(intlist)
45 | return pkt
--------------------------------------------------------------------------------
/pymirai/utils/unpack.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | class Unpack(object):
4 | def __init__(self,pkt=bytes()):
5 | self.pkt = pkt
6 |
7 | def setData(self,pkt):
8 | self.pkt = pkt
9 |
10 | def getBin(self,num):
11 | ret = self.pkt[:num]
12 | self.pkt = self.pkt[num:]
13 | return ret
14 |
15 | def getInt(self):
16 | ret = self.pkt[:4]
17 | self.pkt = self.pkt[4:]
18 | ret = int.from_bytes(ret,byteorder='big')
19 | return ret
20 |
21 | def getShort(self):
22 | ret = self.pkt[:2]
23 | self.pkt = self.pkt[2:]
24 | ret = int.from_bytes(ret,byteorder='big')
25 | return ret
26 |
27 | def getAll(self):
28 | ret = self.pkt
29 | self.pkt = bytes()
30 | return ret
--------------------------------------------------------------------------------
/tests/data.txt:
--------------------------------------------------------------------------------
1 | FC 06 E7 00
2 | 8E 7E C9 8B 92 D6 D9 BB 01 76 6E 2A 2F 71 BA FE
3 | 63 D2 EE D0 AE F0 18 48 70 E5 7A D2 8F 92 1F E7
4 | 56 C4 96 A3 3C 01 9A 1F 70 85 C0 E6 D8 26 27 EE
5 | 7D 90 53 ED AC 8A 4B 83 83 CF 87 25 E9 08 2E 87
6 | 33 C9 45 10 2C B5 B5 2B 0F 57 0E E4 9F 8D 0B 35
7 | 32 3F 02 6F 17 2F 33 2D E0 B8 51 7E 25 04 EA 31
8 | 89 76 75 FE 24 1D AF 9A D0 48 36 7E 14 B8 B1 8B
9 | EE BA 1E 4B 73 5F EC 30 04 A1 4C 88 E2 F7 13 1C
10 | 33 F7 D5 02 08 16 40 85 32 AE 84 46 57 70 B9 01
11 | ED 0E 63 9C 13 71 7E 0C 56 59 DB F3 29 CE DA 7A
12 | D1 A1 53 D5 92 C8 51 40 BA 75 D6 84 A1 7E 75 3C
13 | 98 A7 4B 80 FF 8C 4E 4D 15 63 E8 68 33 17 33 D3
14 | 7D 8E 0A 99 06 9C 3E 51 9C 59 DB E4 45 49 D0 09
15 | C7 9D E2 F2 53 BE 9E 45 F8 3E 7C B4 9B FC 11 A1
16 | 7A 3D 66 7A 0A 23 8F FC ED F1 98 EC C3 37 8B 5C
17 | E6 8E 12 86 E6 AF F1 50 F1 11 3A 94 28 76 EC 7B
18 | 3A C3 44 73 31 B4 AD EA C8 88 08 8B 33 F3 D0 31
19 | 20 86 FD AA 82 B9 CB 7F ED 0D 87 34 4E 3A AC 57
20 | 7E 73 59 D2 AB CA 90 6A BE 80 4C 75 A4 AE FF 48
21 | 96 24 4B 30 21 33 86 A5 E0 A2 D4 35 EE 5D 0B D4
22 | 75 E9 50 85 2D 9A E3 AC FD 76 D1 77 0D 40 CD 17
23 | FC 75 AD 5D CD 1C 11 D6 EA 9E 8E 24 5D 3F 92 99
24 | BE 8D 6E D5 EE 0F 80 D7 98 33 38 63 28 C5 25 AB
25 | 73 DD 28 68 67 EF EB B3 0A F8 60 56 63 E2 D8 DB
26 | 5F 9C 93 76 FC ED 65 8C EE 04 6D F7 96 D5 5E 27
27 | B8 DC E0 8B 0A 8B A2 18 7A 02 36 CB 0B 99 A6 A4
28 | DB 99 6B 86 83 1C C2 39 A0 D0 0C EC FA E8 33 70
29 | 36 57 AF AD A1 88 F9 D6 A2 BE 88 73 79 05 E6 34
30 | 57 63 C4 59 04 98 75 00 15 13 88 47 FC 0A 4C 3E
31 | 28 54 EC 92 36 CB A5 A7 AB E8 02 B3 1A 16 F7 A7
32 | 6E 41 07 5B 4B FA 20 2C 95 B9 06 30 73 A6 8A 81
33 | 05 49 A0 6F B8 5E 22 CC 05 2C 23 CC FD 14 70 16
34 | 07 44 88 12 2B CF E5 79 EA E5 D8 EB 5F 63 31 88
35 | 28 15 38 DA 60 CD AB F2 EE 4A 4A E0 D5 3A 7C CA
36 | 45 77 EA D0 E4 C0 3A DF 41 E4 CF 23 54 6C D2 8C
37 | B6 07 34 EA 61 59 55 03 FC 65 D9 33 EA 44 56 49
38 | A7 2F 84 F8 19 6F 6C 60 09 04 C2 FB 42 ED 3A 93
39 | B5 84 E0 4D E6 EA 1C 25 F2 90 D0 08 0A 37 FC BE
40 | DD 07 28 C3 BE 54 7C 92 EE EC 3C B2 A9 B2 A6 2F
41 | 98 A5 03 36 94 19 44 24 20 0E DD 70 10 27 DC A9
42 | 0F A9 DF 52 F3 A8 0F 84 8B BC 35 1A E1 A6 61 06
43 | 99 F4 F3 1D 90 AF 24 50 2C 67 1C 72 54 DE 1E 54
44 | 96 57 CE EF 5C E8 6E 4C 0C E1 6B 57 DA 88 01 B4
45 | 3D A4 BE 36 04 48 D8 CB D4 89 EC EF B8 DE 89 D0
46 | E3 CB 97 61 FA E3 CA 48 6C FD 95 63 F7 38 DC 03
47 | AE 82 C6 2D BD B2 17 E4 CF FD C9 EE 84 87 F4 64
48 | 2A 51 EF 78 5C 0D 15 05 1B 9E 97 96 9C 74 15 34
49 | F6 95 E6 FF C5 35 9C 1B 0F D7 00 91 7D B1 FA 02
50 | 61 03 BF F1 46 CE EB 52 DF EC D8 BE 64 CD 78 43
51 | AC 54 1C DF FD 82 C2 86 4F 48 A0 02 1C 33 B9 CD
52 | 93 E0 D6 8D ED CC 6F 6D 04 39 59 1F B0 C7 71 DD
53 | 59 6F C0 30 0A 25 4A 43 B2 64 0A C3 0D AC 2B BA
54 | 25 40 E2 CF BA D9 C7 4C 5C 83 4B 80 B2 EF 34 9F
55 | E9 B1 D6 4A E0 7D 0F 5B 5E C2 6B 7F EE AF 4E 7C
56 | 5F 17 77 50 35 50 D3 FE 0C 23 07 01 CF 13 BC EA
57 | A8 58 DE 26 66 F5 E6 ED 85 48 91 CC 56 ED A1 98
58 | E7 3D 75 AA 33 4E 73 52 8F 24 1D 42 04 99 73 C0
59 | 82 AB 7E 67 BA 12 BC 9E 94 7C 3E 88 13 41 ED 95
60 | 3E 5E C3 16 10 B7 5B D3 ED 28 C1 0B E2 91 5C CC
61 | A9 8A BF 97 3D 72 44 ED 84 06 7A D5 21 34 7B AE
62 | 76 22 B7 CD 08 70 3D AE 97 4D C3 3E 23 73 77 27
63 | 53 EA 9C 29 14 6E E5 42 8E 56 32 94 2B B7 D0 86
64 | 0A 39 1A B1 AA 22 67 F3 88 C7 78 40 1D 37 1E 67
65 | ED E7 AF 63 EA E8 2B 17 56 93 18 DD 8B 49 88 62
66 | F4 EA C4 78 3F F2 10 4B 9E 63 12 A5 CE 01 53 22
67 | 5A B1 C7 DF 95 B0 75 51 5D 27 F7 29 12 AC E7 70
68 | 3B 49 B8 40 44 BE 5E 05 3A 23 EC 3A 3E 41 D5 23
69 | 29 A1 39 EE 39 15 F1 AD 19 22 A0 75 33 83 1F 21
70 | 12 9A D6 CC B9 D0 70 89 40 94 81 D6 26 86 8B 38
71 | 6A A0 AE 45 07 A3 80 B5 82 D6 1B 52 61 D3 52 B6
72 | 32 A5 A5 8C 40 07 A9 02 63 B2 1D FA A8 7C AB 7A
73 | 5B 64 F2 3F 88 7C E0 92 CD B4 1E 50 C1 A1 80 07
74 | 39 05 48 E7 8C 8F 5D 6A F8 CD D8 66 0F 48 4F 42
75 | BA 70 59 2B A4 A2 62 51 57 95 9B 40 D7 D9 0D D4
76 | 81 68 28 82 31 F2 3C 1D 63 A4 4C 2B A2 45 B1 C7
77 | 7A 85 59 90 4A E7 65 3A 6D C9 D4 BF 34 BF 5C 28
78 | 5F B0 B5 95 C7 5C C0 BA F6 1E 85 B0 61 C6 A7 96
79 | CC 16 14 AA 55 E7 5C 8B AF 16 0E 2B 8E 18 B2 FD
80 | 5B 86 F9 59 09 CC 90 C2 02 78 A5 85 9F 17 26 75
81 | 24 66 11 D8 0E BF 3A E5 0B 3C CB 85 86 EC 42 64
82 | C7 B2 48 AB 13 68 2B 59 71 16 F0 BB E2 91 C4 95
83 | DD 07 F4 A1 C2 77 44 F5 FC 66 C2 31 6D F8 1F 42
84 | 40 38 05 24 2D 2B 47 F9 AC E3 E7 75 A8 D7 56 16
85 | 3C E5 AE 54 F9 85 C8 87 0C 07 E6 30 8A 9D 1F 22
86 | 6A DA 1F EA FA 2D ED 10 F6 9F 6F 24 C6 B3 99 A8
87 | B3 28 21 60 CE C2 68 68 5B AB F0 66 6F CD AC 8D
88 | A9 AB D3 CC 2E 10 D3 E1 CF 28 01 9D 05 4D 2F 63
89 | 2D 82 0A F2 C1 7C AF 11 80 50 90 64 47 7B 8E 03
90 | C2 70 C2 CB 8B FD 29 8B 8C D8 DF 71 B9 DC A0 AF
91 | FB 75 09 F5 41 40 AC 40 66 71 9E 84 95 12 C1 05
92 | DE D3 63 66 CE F0 29 6B 48 2C D7 9A 72 D8 46 F1
93 | 1C 87 AE C1 DD EF B2 25 EA 56 91 6C 77 F6 35 D6
94 | 73 1F 27 1F CD 41 7B 5E 84 98 D5 D7 ED B9 EF 42
95 | 46 F3 59 15 11 4E EB D4 A5 17 8A F3 AE EB 83 AA
96 | 92 6B 26 42 4C 19 56 72 C8 40 67 2F F0 C2 D9 CC
97 | D6 B6 AC C9 86 E4 CA A0 9B 94 EB 2E 2D 7E 98 3C
98 | 7F A4 5E A9 6F 1C 22 4C 97 09 D6 24 8F DF 27 48
99 | F2 FD DE BC 57 F0 9C C7 E2 4B EB 9F 53 A9 C2 3A
100 | 3D F1 7E 5D B7 79 3C EB D1 27 54 4B 7F 17 F9 DC
101 | 1B FB 3C 69 1A 1A 53 84 07 45 12 5F 4B 61 CE 55
102 | FF AC B1 AC 4C 40 6B 48 47 35 BB 78 B0 45 25 6D
103 | F9 56 FF 5E 54 04 83 E5 E2 E0 F9 59 81 A1 BE A6
104 | EB CC 5E E1 BB 35 AD 86 DB A5 7A 44 38 91 81 F1
105 | B1 14 30 67 95 39 36 AB BF 32 E9 DF F8 87 EA A1
106 | 2B E1 2D 01 CE A8 41 4D 0D 8B 67 18 D8 43 FD 41
107 | CB 38 2E 1C
--------------------------------------------------------------------------------
/tests/test_login.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import asyncio
3 |
4 | from pymirai.utils.pack import Pack
5 |
6 |
7 | async def login(qq: int, password: str):
8 | reader, writer = await asyncio.open_connection("113.96.12.224", 8080)
9 | pack = Pack()
10 | pack.write_hex("00 00 06 B4 00 00 00 0A 02 00 00 00 04 00 00 00 00 0E")
11 | pack.write_qq(qq)
12 | writer.write(pack.get_all())
13 | await writer.drain()
--------------------------------------------------------------------------------
/tests/test_pack.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import unittest
3 | from unittest import TestCase
4 |
5 | from pymirai.utils.tools import *
6 | from pymirai.utils.pack import Pack
7 | from pymirai.utils.tea import Tea
8 |
9 |
10 | class TestPack(TestCase):
11 |
12 | def test_pack(self):
13 | a = bytes2hex(Tea.encrypt(str2bytes('哈哈哈'), hex2bytes('12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12')))
14 | # print(a)
15 | b = bytes2hex(Tea.decrypt(hex2bytes(a), hex2bytes('12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12')))
16 | self.assertEqual(hex2str(b), "哈哈哈")
17 | p = Pack()
18 | p.write_qq(123456)
19 | self.assertEqual(p.get_all(), hex2bytes("31 32 33 34 35 36"))
20 |
21 | def test_decode(self):
22 | with open("data.txt") as f:
23 | a = f.read().strip()
24 | data = Tea.decrypt((hex2bytes(a)), bytes(16))
25 | with open("login", "wb") as f2:
26 | f2.write(data)
27 | pass
28 |
29 |
30 | if __name__ == "__main__":
31 | unittest.main()
32 |
--------------------------------------------------------------------------------
/tests/test_signal.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import unittest
3 | import asyncio
4 | from unittest import IsolatedAsyncioTestCase
5 |
6 | from pymirai.signals import SignalManager
7 |
8 |
9 | class TestPack(IsolatedAsyncioTestCase):
10 | async def a(self, t):
11 | await asyncio.sleep(t)
12 | print(f"收到了a信号 {t}")
13 | return t
14 |
15 | async def b(self, t):
16 | await asyncio.sleep(t)
17 | print(f"收到了b信号 {t}")
18 | return t
19 |
20 | async def asyncSetUp(self) -> None:
21 | self.signal = SignalManager()
22 | self.signal.connect(self.a, "a")
23 | self.signal.connect(self.b, "b")
24 |
25 | async def test_a(self):
26 | data = await self.signal.send_async("a", 1)
27 | self.assertEqual(len(data), 1, "接收到信号的函数个数不正确")
28 | self.assertEqual(data[0][0], self.a, "错误的函数接收到了信号")
29 | self.assertEqual(data[0][1], 1, "返回值错误")
30 |
31 | async def test_b(self):
32 | data = await self.signal.send_async("b", 1)
33 | self.assertEqual(len(data), 1, "接收到信号的函数个数不正确")
34 | self.assertEqual(data[0][0], self.b, "错误的函数接收到了信号")
35 | self.assertEqual(data[0][1], 1, "返回值错误")
36 |
37 |
38 | if __name__ == "__main__":
39 | unittest.main()
40 |
--------------------------------------------------------------------------------