作者 lemon

新增socks5代理支持,新增获取安全设备记录接口

1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 import sys, os 2 import sys, os
  3 +import fbchat._mqtt
3 4
4 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 5 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5 sys.path.append(BASE_DIR) 6 sys.path.append(BASE_DIR)
@@ -7,6 +8,17 @@ sys.path.append(BASE_DIR) @@ -7,6 +8,17 @@ sys.path.append(BASE_DIR)
7 from core import core 8 from core import core
8 from conf import log_settings 9 from conf import log_settings
9 10
  11 +
  12 +def reload_mqtt():
  13 + '''替换_mqtt模块,增加Proxy支持'''
  14 + import importlib.machinery, importlib.util
  15 + loader = importlib.machinery.SourceFileLoader('fbchat._mqtt', 'lib/_mqtt.py')
  16 + spec = importlib.util.spec_from_loader(loader.name, loader)
  17 + fbchat._mqtt = importlib.util.module_from_spec(spec)
  18 + loader.exec_module(fbchat._mqtt)
  19 +
  20 +
10 if __name__ == '__main__': 21 if __name__ == '__main__':
11 # log_settings.load_logging_cfg().info("Running") 22 # log_settings.load_logging_cfg().info("Running")
  23 + reload_mqtt()
12 core.run() 24 core.run()
@@ -16,6 +16,11 @@ from core.monitor import Monitor @@ -16,6 +16,11 @@ from core.monitor import Monitor
16 from lib import control_server 16 from lib import control_server
17 from lib.socket_ import MessageSocketClient 17 from lib.socket_ import MessageSocketClient
18 18
  19 +try:
  20 + from lib.message_queue import MessageQueue as Queue
  21 +except:
  22 + Queue = None
  23 +
19 monitor = Monitor() 24 monitor = Monitor()
20 monitor.version = settings.get_version() 25 monitor.version = settings.get_version()
21 26
@@ -94,4 +99,9 @@ def run(): @@ -94,4 +99,9 @@ def run():
94 sched.start() 99 sched.start()
95 # init schedule end 100 # init schedule end
96 101
  102 + # message queue start
  103 + if Queue:
  104 + monitor.queue = Queue(monitor._name, monitor.execute).start()
  105 + # message queue end
  106 +
97 ioloop.IOLoop.instance().start() 107 ioloop.IOLoop.instance().start()
@@ -41,6 +41,7 @@ class Monitor(callback.CallBack): @@ -41,6 +41,7 @@ class Monitor(callback.CallBack):
41 self.version = None 41 self.version = None
42 self.executor = ThreadPoolExecutor(50, 'task_thread') 42 self.executor = ThreadPoolExecutor(50, 'task_thread')
43 self.init_config = {} 43 self.init_config = {}
  44 + self.queue = None
44 45
45 def bind(self, socket): 46 def bind(self, socket):
46 self._socket = socket 47 self._socket = socket
@@ -147,6 +148,8 @@ class Monitor(callback.CallBack): @@ -147,6 +148,8 @@ class Monitor(callback.CallBack):
147 if hasattr(client, 'uid') and client.uid: 148 if hasattr(client, 'uid') and client.uid:
148 data['fbid'] = client.uid 149 data['fbid'] = client.uid
149 payload = add_type("notify", data) 150 payload = add_type("notify", data)
  151 +
  152 + if self.queue: self.queue.publish_msg(data)
150 self._socket.send(payload) 153 self._socket.send(payload)
151 154
152 def _task_(self, type_, client, taskid: int, code, msg: dict = None): 155 def _task_(self, type_, client, taskid: int, code, msg: dict = None):
  1 +import re
  2 +
  3 +import attr
  4 +import random
  5 +import paho.mqtt.client
  6 +import socks
  7 +from fbchat._core import log
  8 +from fbchat import _util, _exception, _graphql
  9 +
  10 +import ssl
  11 +
  12 +ssl._create_default_https_context = ssl._create_unverified_context
  13 +
  14 +
  15 +def generate_session_id():
  16 + """Generate a random session ID between 1 and 9007199254740991."""
  17 + return random.randint(1, 2 ** 53)
  18 +
  19 +
  20 +@attr.s(slots=True)
  21 +class Mqtt(object):
  22 + _state = attr.ib()
  23 + _mqtt = attr.ib()
  24 + _on_message = attr.ib()
  25 + _chat_on = attr.ib()
  26 + _foreground = attr.ib()
  27 + _sequence_id = attr.ib()
  28 + _sync_token = attr.ib(None)
  29 +
  30 + _HOST = "edge-chat.facebook.com"
  31 +
  32 + @classmethod
  33 + def connect(cls, state, on_message, chat_on, foreground):
  34 + mqtt = paho.mqtt.client.Client(
  35 + client_id="mqttwsclient",
  36 + clean_session=True,
  37 + protocol=paho.mqtt.client.MQTTv31,
  38 + transport="websockets",
  39 + )
  40 + mqtt.enable_logger()
  41 +
  42 + if state._session.proxies:
  43 + proxy = state._session.params.get('sock')
  44 + mqtt.proxy_set(**proxy)
  45 + # mqtt.max_inflight_messages_set(20) # The rest will get queued
  46 + # mqtt.max_queued_messages_set(0) # Unlimited messages can be queued
  47 + # mqtt.message_retry_set(20) # Retry sending for at least 20 seconds
  48 + # mqtt.reconnect_delay_set(min_delay=1, max_delay=120)
  49 + # TODO: Is region (lla | atn | odn | others?) important?
  50 + mqtt.tls_set()
  51 +
  52 + self = cls(
  53 + state=state,
  54 + mqtt=mqtt,
  55 + on_message=on_message,
  56 + chat_on=chat_on,
  57 + foreground=foreground,
  58 + sequence_id=cls._fetch_sequence_id(state),
  59 + )
  60 +
  61 + # Configure callbacks
  62 + mqtt.on_message = self._on_message_handler
  63 + mqtt.on_connect = self._on_connect_handler
  64 +
  65 + self._configure_connect_options()
  66 +
  67 + # Attempt to connect
  68 + try:
  69 + rc = mqtt.connect(self._HOST, 443, keepalive=10)
  70 + except (
  71 + # Taken from .loop_forever
  72 + paho.mqtt.client.socket.error,
  73 + OSError,
  74 + paho.mqtt.client.WebsocketConnectionError,
  75 + ) as e:
  76 + raise _exception.FBchatException("MQTT connection failed")
  77 +
  78 + # Raise error if connecting failed
  79 + if rc != paho.mqtt.client.MQTT_ERR_SUCCESS:
  80 + err = paho.mqtt.client.error_string(rc)
  81 + raise _exception.FBchatException("MQTT connection failed: {}".format(err))
  82 +
  83 + return self
  84 +
  85 + def _on_message_handler(self, client, userdata, message):
  86 + # Parse payload JSON
  87 + try:
  88 + j = _util.parse_json(message.payload.decode("utf-8"))
  89 + except (_exception.FBchatFacebookError, UnicodeDecodeError):
  90 + log.exception("Failed parsing MQTT data on %s as JSON", message.topic)
  91 + return
  92 +
  93 + if message.topic == "/t_ms":
  94 + # Update sync_token when received
  95 + # This is received in the first message after we've created a messenger
  96 + # sync queue.
  97 + if "syncToken" in j and "firstDeltaSeqId" in j:
  98 + self._sync_token = j["syncToken"]
  99 + self._sequence_id = j["firstDeltaSeqId"]
  100 +
  101 + # Update last sequence id when received
  102 + if "lastIssuedSeqId" in j:
  103 + self._sequence_id = j["lastIssuedSeqId"]
  104 +
  105 + if "errorCode" in j:
  106 + # Known types: ERROR_QUEUE_OVERFLOW | ERROR_QUEUE_NOT_FOUND
  107 + # 'F\xfa\x84\x8c\x85\xf8\xbc-\x88 FB_PAGES_INSUFFICIENT_PERMISSION\x00'
  108 + log.error("MQTT error code %s received", j["errorCode"])
  109 + # TODO: Consider resetting the sync_token and sequence ID here?
  110 +
  111 + log.debug("MQTT payload: %s, %s", message.topic, j)
  112 +
  113 + # Call the external callback
  114 + self._on_message(message.topic, j)
  115 +
  116 + @staticmethod
  117 + def _fetch_sequence_id(state):
  118 + """Fetch sequence ID."""
  119 + params = {
  120 + "limit": 1,
  121 + "tags": ["INBOX"],
  122 + "before": None,
  123 + "includeDeliveryReceipts": False,
  124 + "includeSeqID": True,
  125 + }
  126 + log.debug("Fetching MQTT sequence ID")
  127 + # Same request as in `Client.fetchThreadList`
  128 + (j,) = state._graphql_requests(_graphql.from_doc_id("1349387578499440", params))
  129 + try:
  130 + return int(j["viewer"]["message_threads"]["sync_sequence_id"])
  131 + except (KeyError, ValueError):
  132 + # TODO: Proper exceptions
  133 + raise
  134 +
  135 + def _on_connect_handler(self, client, userdata, flags, rc):
  136 + if rc == 21:
  137 + raise _exception.FBchatException(
  138 + "Failed connecting. Maybe your cookies are wrong?"
  139 + )
  140 + if rc != 0:
  141 + return # Don't try to send publish if the connection failed
  142 +
  143 + # configure receiving messages.
  144 + payload = {
  145 + "sync_api_version": 10,
  146 + "max_deltas_able_to_process": 1000,
  147 + "delta_batch_size": 500,
  148 + "encoding": "JSON",
  149 + "entity_fbid": self._state.user_id,
  150 + }
  151 +
  152 + # If we don't have a sync_token, create a new messenger queue
  153 + # This is done so that across reconnects, if we've received a sync token, we
  154 + # SHOULD receive a piece of data in /t_ms exactly once!
  155 + if self._sync_token is None:
  156 + topic = "/messenger_sync_create_queue"
  157 + payload["initial_titan_sequence_id"] = str(self._sequence_id)
  158 + payload["device_params"] = None
  159 + else:
  160 + topic = "/messenger_sync_get_diffs"
  161 + payload["last_seq_id"] = str(self._sequence_id)
  162 + payload["sync_token"] = self._sync_token
  163 +
  164 + self._mqtt.publish(topic, _util.json_minimal(payload), qos=1)
  165 +
  166 + def _configure_connect_options(self):
  167 + # Generate a new session ID on each reconnect
  168 + session_id = generate_session_id()
  169 +
  170 + topics = [
  171 + # Things that happen in chats (e.g. messages)
  172 + "/t_ms",
  173 + # Group typing notifications
  174 + "/thread_typing",
  175 + # Private chat typing notifications
  176 + "/orca_typing_notifications",
  177 + # Active notifications
  178 + "/orca_presence",
  179 + # Other notifications not related to chats (e.g. friend requests)
  180 + "/legacy_web",
  181 + # Facebook's continuous error reporting/logging?
  182 + "/br_sr",
  183 + # Response to /br_sr
  184 + "/sr_res",
  185 + # TODO: Investigate the response from this! (A bunch of binary data)
  186 + # "/t_p",
  187 + # TODO: Find out what this does!
  188 + "/webrtc",
  189 + # TODO: Find out what this does!
  190 + "/onevc",
  191 + # TODO: Find out what this does!
  192 + "/notify_disconnect",
  193 + # Old, no longer active topics
  194 + # These are here just in case something interesting pops up
  195 + "/inbox",
  196 + "/mercury",
  197 + "/messaging_events",
  198 + "/orca_message_notifications",
  199 + "/pp",
  200 + "/t_rtc",
  201 + "/webrtc_response",
  202 + ]
  203 +
  204 + username = {
  205 + # The user ID
  206 + "u": self._state.user_id,
  207 + # Session ID
  208 + "s": session_id,
  209 + # Active status setting
  210 + "chat_on": self._chat_on,
  211 + # foreground_state - Whether the window is focused
  212 + "fg": self._foreground,
  213 + # Can be any random ID
  214 + "d": self._state._client_id,
  215 + # Application ID, taken from facebook.com
  216 + "aid": 219994525426954,
  217 + # MQTT extension by FB, allows making a SUBSCRIBE while CONNECTing
  218 + "st": topics,
  219 + # MQTT extension by FB, allows making a PUBLISH while CONNECTing
  220 + # Using this is more efficient, but the same can be acheived with:
  221 + # def on_connect(*args):
  222 + # mqtt.publish(topic, payload, qos=1)
  223 + # mqtt.on_connect = on_connect
  224 + # TODO: For some reason this doesn't work!
  225 + "pm": [
  226 + # {
  227 + # "topic": topic,
  228 + # "payload": payload,
  229 + # "qos": 1,
  230 + # "messageId": 65536,
  231 + # }
  232 + ],
  233 + # Unknown parameters
  234 + "cp": 3,
  235 + "ecp": 10,
  236 + "ct": "websocket",
  237 + "mqtt_sid": "",
  238 + "dc": "",
  239 + "no_auto_fg": True,
  240 + "gas": None,
  241 + "pack": [],
  242 + }
  243 +
  244 + # TODO: Make this thread safe
  245 + self._mqtt.username_pw_set(_util.json_minimal(username))
  246 +
  247 + headers = {
  248 + # TODO: Make this access thread safe
  249 + "Cookie": _util.get_cookie_header(
  250 + self._state._session, "https://edge-chat.facebook.com/chat"
  251 + ),
  252 + "User-Agent": self._state._session.headers["User-Agent"],
  253 + "Origin": "https://www.facebook.com",
  254 + "Host": self._HOST,
  255 + }
  256 +
  257 + self._mqtt.ws_set_options(
  258 + path="/chat?sid={}".format(session_id), headers=headers
  259 + )
  260 +
  261 + def loop_once(self, on_error=None):
  262 + """Run the listening loop once.
  263 +
  264 + Returns whether to keep listening or not.
  265 + """
  266 + rc = self._mqtt.loop(timeout=1.0)
  267 +
  268 + # If disconnect() has been called
  269 + if self._mqtt._state == paho.mqtt.client.mqtt_cs_disconnecting:
  270 + return False # Stop listening
  271 +
  272 + if rc != paho.mqtt.client.MQTT_ERR_SUCCESS:
  273 + # If known/expected error
  274 + if rc == paho.mqtt.client.MQTT_ERR_CONN_LOST:
  275 + log.warning("Connection lost, retrying")
  276 + elif rc == paho.mqtt.client.MQTT_ERR_NOMEM:
  277 + # This error is wrongly classified
  278 + # See https://github.com/eclipse/paho.mqtt.python/issues/340
  279 + log.warning("Connection error, retrying")
  280 + else:
  281 + err = paho.mqtt.client.error_string(rc)
  282 + log.error("MQTT Error: %s", err)
  283 + # For backwards compatibility
  284 + if on_error:
  285 + on_error(_exception.FBchatException("MQTT Error {}".format(err)))
  286 +
  287 + # Wait before reconnecting
  288 + self._mqtt._reconnect_wait()
  289 +
  290 + # Try reconnecting
  291 + self._configure_connect_options()
  292 + try:
  293 + self._mqtt.reconnect()
  294 + except (
  295 + # Taken from .loop_forever
  296 + paho.mqtt.client.socket.error,
  297 + OSError,
  298 + paho.mqtt.client.WebsocketConnectionError,
  299 + ) as e:
  300 + log.debug("MQTT reconnection failed: %s", e)
  301 +
  302 + return True # Keep listening
  303 +
  304 + def disconnect(self):
  305 + self._mqtt.disconnect()
  306 +
  307 + def set_foreground(self, value):
  308 + payload = _util.json_minimal({"foreground": value})
  309 + info = self._mqtt.publish("/foreground_state", payload=payload, qos=1)
  310 + self._foreground = value
  311 + # TODO: We can't wait for this, since the loop is running with .loop_forever()
  312 + # info.wait_for_publish()
  313 +
  314 + def set_chat_on(self, value):
  315 + # TODO: Is this the right request to make?
  316 + data = {"make_user_available_when_in_foreground": value}
  317 + payload = _util.json_minimal(data)
  318 + info = self._mqtt.publish("/set_client_settings", payload=payload, qos=1)
  319 + self._chat_on = value
  320 + # TODO: We can't wait for this, since the loop is running with .loop_forever()
  321 + # info.wait_for_publish()
  322 +
  323 + # def send_additional_contacts(self, additional_contacts):
  324 + # payload = _util.json_minimal({"additional_contacts": additional_contacts})
  325 + # info = self._mqtt.publish("/send_additional_contacts", payload=payload, qos=1)
  326 + #
  327 + # def browser_close(self):
  328 + # info = self._mqtt.publish("/browser_close", payload=b"{}", qos=1)
@@ -104,7 +104,6 @@ class College(): @@ -104,7 +104,6 @@ class College():
104 data["concentration_ids[%d]" % i] = sub[1] 104 data["concentration_ids[%d]" % i] = sub[1]
105 return data 105 return data
106 106
107 -  
108 @classmethod 107 @classmethod
109 def from_dict(cls, data: dict): 108 def from_dict(cls, data: dict):
110 self = cls() 109 self = cls()
@@ -176,7 +175,7 @@ def todict(obj, include: list = None): @@ -176,7 +175,7 @@ def todict(obj, include: list = None):
176 return res 175 return res
177 176
178 177
179 -def tobase64(obj): 178 +def tobase64(obj) -> str:
180 if isinstance(obj, (dict, list)): 179 if isinstance(obj, (dict, list)):
181 obj = demjson.encode(obj) 180 obj = demjson.encode(obj)
182 if isinstance(obj, str): 181 if isinstance(obj, str):
@@ -187,6 +186,66 @@ def tobase64(obj): @@ -187,6 +186,66 @@ def tobase64(obj):
187 raise BaseException("must str,list,dict,bytes") 186 raise BaseException("must str,list,dict,bytes")
188 187
189 188
190 -def frombase64(base): 189 +def frombase64(base) -> dict:
191 string = base64.b64decode(base).decode() 190 string = base64.b64decode(base).decode()
192 return demjson.decode(string) 191 return demjson.decode(string)
  192 +
  193 +
  194 +def format_proxies(conf: dict, format='requests'):
  195 + '''
  196 + e.g.
  197 + {
  198 + 'type': 'socks5',
  199 + 'host': '47.56.152.111',
  200 + 'pass': 'nantian888',
  201 + 'port': '1080',
  202 + 'user': 'ntkj'
  203 + }
  204 + '''
  205 + type_ = conf.get('type', None)
  206 + host = conf.get('host', None)
  207 + port = conf.get('port', None)
  208 + user = conf.get('user', None)
  209 + pass_ = conf.get('pass', None)
  210 +
  211 + if format == 'requests':
  212 + info = []
  213 + info.append(type_)
  214 + info.append("://")
  215 + p_auth = []
  216 + if user:
  217 + p_auth.append(str(user))
  218 + p_auth.append(":")
  219 + if pass_:
  220 + p_auth.append(str(pass_))
  221 + if p_auth:
  222 + info.append(''.join(p_auth))
  223 + info.append('@')
  224 + info.append(host)
  225 + if port:
  226 + info.append(':')
  227 + info.append(str(port))
  228 + text = ''.join(info)
  229 + return {'http': text, 'https': text}
  230 + else:
  231 + _type_ = getattr(__import__('socks'), 'PROXY_TYPES').get(type_.upper())
  232 + assert _type_, '代理协议错误,可选socks5,socks4,http'
  233 + proxy = {
  234 + "proxy_type": _type_,
  235 + "proxy_addr": host,
  236 + "proxy_port": int(port),
  237 + }
  238 + if user:
  239 + proxy['proxy_username'] = user
  240 + if pass_:
  241 + proxy['proxy_password'] = pass_
  242 + return proxy
  243 +
  244 +
  245 +if __name__ == '__main__':
  246 + conf = {'type': 'http',
  247 + 'host': '47.56.152.111',
  248 + 'pass': 'nantian888',
  249 + 'port': '1080',
  250 + 'user': 'ntkj'}
  251 + print(format_proxies(conf, '1'))
@@ -14,10 +14,12 @@ from enum import Enum @@ -14,10 +14,12 @@ from enum import Enum
14 14
15 import demjson 15 import demjson
16 import furl 16 import furl
  17 +import requests
17 from fbchat import Client, ThreadType, Message, Sticker, FBchatUserError, _exception, log, _util 18 from fbchat import Client, ThreadType, Message, Sticker, FBchatUserError, _exception, log, _util
18 from fbchat._state import State, session_factory, is_home 19 from fbchat._state import State, session_factory, is_home
19 20
20 from lib import google_map, common 21 from lib import google_map, common
  22 +from lib._mqtt import Mqtt
21 from lib.common import WorkPlace, College 23 from lib.common import WorkPlace, College
22 from utils import parse_html, _attachment 24 from utils import parse_html, _attachment
23 25
@@ -25,10 +27,16 @@ from utils import parse_html, _attachment @@ -25,10 +27,16 @@ from utils import parse_html, _attachment
25 class PCState(State): 27 class PCState(State):
26 28
27 @classmethod 29 @classmethod
28 - def login(cls, email, password, on_2fa_callback, user_agent=None): 30 + def login(cls, email, password, on_2fa_callback, user_agent=None, proxy=None):
29 '''换成了PC的登录方式''' 31 '''换成了PC的登录方式'''
30 session = session_factory(user_agent=user_agent) 32 session = session_factory(user_agent=user_agent)
31 33
  34 + if proxy:
  35 + short_proxy = common.format_proxies(proxy, 'requests')
  36 + long_proxy = common.format_proxies(proxy, 'sock')
  37 + session.params.update({'sock': long_proxy})
  38 + session.proxies.update(short_proxy)
  39 +
32 soup = parse_html.find_input_fields_with_pc(session.get("https://www.facebook.com/").text) 40 soup = parse_html.find_input_fields_with_pc(session.get("https://www.facebook.com/").text)
33 data = dict((elem["name"], elem["value"]) 41 data = dict((elem["name"], elem["value"])
34 for elem in soup 42 for elem in soup
@@ -71,6 +79,19 @@ class PCState(State): @@ -71,6 +79,19 @@ class PCState(State):
71 return "Location" in r.headers and is_home(r.headers["Location"]) 79 return "Location" in r.headers and is_home(r.headers["Location"])
72 80
73 @classmethod 81 @classmethod
  82 + def from_cookies(cls, cookies, user_agent=None, proxy=None):
  83 + session = session_factory(user_agent=user_agent)
  84 + session.cookies = requests.cookies.merge_cookies(session.cookies, cookies)
  85 +
  86 + if proxy:
  87 + short_proxy = common.format_proxies(proxy, 'requests')
  88 + long_proxy = common.format_proxies(proxy, 'sock')
  89 +
  90 + session.params.update({'sock': long_proxy})
  91 + session.proxies.update(short_proxy)
  92 + return cls.from_session(session=session)
  93 +
  94 + @classmethod
74 def from_session(cls, session): 95 def from_session(cls, session):
75 96
76 def get_user_id(session): 97 def get_user_id(session):
@@ -81,7 +102,7 @@ class PCState(State): @@ -81,7 +102,7 @@ class PCState(State):
81 102
82 user_id = get_user_id(session) 103 user_id = get_user_id(session)
83 104
84 - r = session.get(_util.prefix_url("/"), timeout=10) 105 + r = session.get(_util.prefix_url("/"), timeout=30)
85 106
86 b = parse_html.show_home_page(r.text) 107 b = parse_html.show_home_page(r.text)
87 logout_menu = b.find('div', id='logoutMenu') 108 logout_menu = b.find('div', id='logoutMenu')
@@ -171,6 +192,7 @@ class FacebookClient(Client): @@ -171,6 +192,7 @@ class FacebookClient(Client):
171 self.email = user_obj.email 192 self.email = user_obj.email
172 self.user_obj = user_obj 193 self.user_obj = user_obj
173 self.extend = None 194 self.extend = None
  195 + self.proxy = user_obj.proxy if hasattr(user_obj, 'proxy') else None
174 196
175 super().__init__(user_obj.email, user_obj.password, user_obj.user_agent, max_tries, user_obj.format_cookie()) 197 super().__init__(user_obj.email, user_obj.password, user_obj.user_agent, max_tries, user_obj.format_cookie())
176 198
@@ -179,7 +201,7 @@ class FacebookClient(Client): @@ -179,7 +201,7 @@ class FacebookClient(Client):
179 201
180 def setSession(self, session_cookies, user_agent=None): 202 def setSession(self, session_cookies, user_agent=None):
181 try: 203 try:
182 - self._state = PCState.from_cookies(session_cookies, user_agent=user_agent) 204 + self._state = PCState.from_cookies(session_cookies, user_agent=user_agent, proxy=self.proxy)
183 self._uid = self._state.user_id 205 self._uid = self._state.user_id
184 except Exception as e: 206 except Exception as e:
185 log.exception("Failed loading session") 207 log.exception("Failed loading session")
@@ -203,6 +225,7 @@ class FacebookClient(Client): @@ -203,6 +225,7 @@ class FacebookClient(Client):
203 password, 225 password,
204 on_2fa_callback=self.on2FACode, 226 on_2fa_callback=self.on2FACode,
205 user_agent=user_agent, 227 user_agent=user_agent,
  228 + proxy=self.proxy
206 ) 229 )
207 self._uid = self._state.user_id 230 self._uid = self._state.user_id
208 except Exception as err: 231 except Exception as err:
@@ -1010,3 +1033,21 @@ class FacebookClient(Client): @@ -1010,3 +1033,21 @@ class FacebookClient(Client):
1010 'ext_data': next_data 1033 'ext_data': next_data
1011 } 1034 }
1012 return response 1035 return response
  1036 +
  1037 + def startListening(self):
  1038 + if not self._mqtt:
  1039 + self._mqtt = Mqtt.connect(
  1040 + state=self._state,
  1041 + on_message=self._parse_message,
  1042 + chat_on=self._markAlive,
  1043 + foreground=True,
  1044 + )
  1045 + self.onQprimer(ts=int(time.time() * 1000), msg=None)
  1046 + self.listening = True
  1047 +
  1048 + def securityDevice(self):
  1049 + data = {'av': self.uid, 'fb_api_caller_class': 'RelayModern',
  1050 + 'fb_api_req_friendly_name': 'SecuritySettingsSessionGroupRefetchQuery',
  1051 + 'variables': '{"session_count":20}', 'doc_id': '2549907381730805'}
  1052 + res = self._post('https://www.facebook.com/api/graphql/', data)
  1053 + return res['data']['security_settings']['sessions']
@@ -73,6 +73,8 @@ class UserList(Base): @@ -73,6 +73,8 @@ class UserList(Base):
73 fbid = Column(String(20), index=True) 73 fbid = Column(String(20), index=True)
74 status = Column(Integer, default=0, nullable=False, index=True) 74 status = Column(Integer, default=0, nullable=False, index=True)
75 75
  76 + # proxy = Column(String(256))
  77 +
76 def __repr__(self): 78 def __repr__(self):
77 return "User(id={}, email={}, password={}, cookie={}, fbid={}, status={})" \ 79 return "User(id={}, email={}, password={}, cookie={}, fbid={}, status={})" \
78 .format(self.id, self.email, self.password, len(self.cookie) if self.cookie else None, self.fbid, 80 .format(self.id, self.email, self.password, len(self.cookie) if self.cookie else None, self.fbid,
@@ -9,3 +9,4 @@ sqlalchemy==1.3.12 @@ -9,3 +9,4 @@ sqlalchemy==1.3.12
9 psutil==5.6.7 9 psutil==5.6.7
10 demjson==2.2.4 10 demjson==2.2.4
11 apscheduler==3.6.3 11 apscheduler==3.6.3
  12 +pysocks==1.7.1