-
-
Notifications
You must be signed in to change notification settings - Fork 306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rotating proxy functionality #139
Changes from all commits
79e4985
d5c951d
f468ff0
9daa230
8a98ba8
cb6f290
9966944
0825191
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
requests==2.25.1 | ||
gate_api==4.22.2 | ||
PyYAML==6.0 | ||
PySocks==1.7.1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
import requests | ||
|
||
from gateio_new_coins_announcements_bot.logger import logger | ||
from gateio_new_coins_announcements_bot.rotating_proxy import get_proxy | ||
from gateio_new_coins_announcements_bot.rotating_proxy import is_ready as rotating_proxy_is_ready | ||
from gateio_new_coins_announcements_bot.util.random import random_int | ||
from gateio_new_coins_announcements_bot.util.random import random_str | ||
|
||
|
@@ -18,7 +20,17 @@ def fetch_latest_announcement(self): | |
""" | ||
logger.debug("Pulling announcement page") | ||
request_url = self.__request_url() | ||
response = self.http_client.get(request_url) | ||
|
||
if rotating_proxy_is_ready(): | ||
proxy = get_proxy() | ||
logger.debug(f"Using proxy: {proxy}") | ||
try: | ||
response = self.http_client.get(request_url, proxies={"http": "socks5://" + proxy}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another point we should consider is what happens when the request via the proxy fails or returns status code 429. |
||
|
||
except Exception as e: | ||
logger.error(e) | ||
else: | ||
response = self.http_client.get(request_url) | ||
|
||
# Raise an HTTPError if status is not 200 | ||
response.raise_for_status() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import itertools | ||
import socket | ||
import struct | ||
import threading | ||
import time | ||
from typing import Callable | ||
|
||
import requests | ||
|
||
import gateio_new_coins_announcements_bot.globals as globals | ||
from gateio_new_coins_announcements_bot.logger import logger | ||
|
||
|
||
_proxy_list = {} | ||
Linus045 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_proxy = None | ||
_event = threading.Event() | ||
|
||
|
||
def init_proxy(): | ||
threading.Thread(target=lambda: _every(60 * 10, _fetch_proxies)).start() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we might wanna make the poll delay also configurable via the config. |
||
# Required for populating the proxy list when starting bot | ||
_fetch_proxies() | ||
|
||
|
||
def _fetch_proxies(): | ||
logger.info("Fetching proxies...") | ||
global _proxy | ||
threads: list[threading.Thread] = [] | ||
try: | ||
proxy_res = requests.get( | ||
"https://www.proxyscan.io/api/proxy?last_check=180&limit=20&type=socks5&format=txt&ping=1000" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we might also wanna consider a different last_check value, but thats something we can adjust after more testing as well. |
||
).text | ||
except requests.exceptions.RequestException as e: | ||
logger.debug(f"Can't fetch proxies. Reason: {e}") | ||
return | ||
|
||
# Merging old proxies with new ones | ||
_merged_proxies = list(proxy_res[:-1].split("\n") | _proxy_list.keys()) | ||
|
||
if len(_merged_proxies) > 0: | ||
for p in _merged_proxies: | ||
t = threading.Thread(target=checker, args=[p]) | ||
t.start() | ||
threads.append(t) | ||
|
||
for t in threads: | ||
t.join() | ||
|
||
logger.info(f"Fetched {len(_proxy_list)} proxies") | ||
_proxy = itertools.cycle(_proxy_list.copy().keys()) | ||
|
||
|
||
def get_proxy() -> str: | ||
try: | ||
return next(_proxy) | ||
except StopIteration as exc: | ||
raise Exception("No proxies available") from exc | ||
|
||
|
||
def is_ready() -> bool: | ||
return len(_proxy_list) > 0 | ||
|
||
|
||
def set_proxy_event(): | ||
_event.set() | ||
|
||
|
||
# can be generalized and moved to separate file | ||
def _every(delay: int, task: Callable): | ||
global _event | ||
next_time = time.time() + delay | ||
while not globals.stop_threads: | ||
_event.wait(max(0, next_time - time.time())) | ||
if not globals.stop_threads: | ||
try: | ||
task() | ||
except Exception as e: | ||
logger.error("Problem while fetching proxies") | ||
andreademasi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
logger.debug(e) | ||
# skip tasks if we are behind schedule: | ||
next_time += (time.time() - next_time) // delay * delay + delay | ||
logger.info("Proxies fetching thread has stopped.") | ||
|
||
|
||
def checker(proxy: str): | ||
global _proxy_list | ||
ip, port = proxy.split(":") | ||
sen = struct.pack("BBB", 0x05, 0x01, 0x00) | ||
|
||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: | ||
s.settimeout(5) | ||
try: | ||
s.connect((ip, int(port))) | ||
s.sendall(sen) | ||
|
||
data = s.recv(2) | ||
version, auth = struct.unpack("BB", data) | ||
# Check if the proxy is socks5 and it doesn't require authentication | ||
if version == 5 and auth == 0: | ||
_proxy_list[proxy] = proxy | ||
else: | ||
_proxy_list.pop(proxy, None) | ||
|
||
except Exception as e: | ||
logger.info(f"Proxy {proxy} invalid. Reason: {e}") | ||
_proxy_list.pop(proxy, None) | ||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be a logger.info combined with the first message e.g.:
when using proxy: "Getting Binance announcements [Using proxy: 127.0.0.1]"
when no valid proxy exists: "Getting Binance announcements [No proxy available]"
when the proxy feature is disabled: "Getting Binance announcements"
That being said, it should be implemented in a new PR and not here.
I was planning on reworking the console logs at some point anyway so we should consider this then as well.