- Регистрация
- 27.11.2023
- Сообщения
- 45
- Благодарностей
- 96
- Баллы
- 18
intro {
Всем привет, господа автоматизаторы

Это моя третья работа и, наверное, некий самоотчет по прогрессу.
Лирический дисклеймер
В качестве вводных отмечу, что в материал вложены полтора года непрерывного опыта разработки в ZP, начатого с околонулевым пониманием как программирования, так и автоматизации в целом.
Исходя из личного опыта (у вас он может быть другой) - могу сказать, что нет единого наилучшего формата обучения, который бы заходил всем. Кому-то нужно понять концепции и тогда можно как-то обработать объекты для обучения, а кому-то наоборот, любая концепция - туман, пока она не материализовалась в прикладную задачу из его собственной, а не абстрактной реальности.
Начинал я с кубиков, постепенно внедряя по необходимости решения на C# от AI или из чужих шаблонов. Также по необходимости я модифицировал эти решения, попутно разбираясь, как работает та или иная область в реальной ситуации, которая возникла не как самоцель обучения, а как реальный кейс, который мне нужно уметь обрабатывать в свете специфики именно моей деятельности, а не, к примеру, рассылок, которыми я не занимаюсь.
Да, про ООП мне все же пришлось покурить теоретический материал - но в целом процентов 90 знаний были получены и переварены в работе на разборе уже работающего в данный момент кода.
// TL;DR
Понимание принципов работы C# не необходимо для работы с готовым кодом С#, и если ты не "шаришь" в коде не спеши закрывать статью. Возможно именно использование функционала отсюда и прояснит для тебя некоторые вещи (также помним, что не пользоваться AI в наше время - все равно что хранить пульт от телевизора в пакете с пупырками, так даже бабули уже не все делают)
К делу!
В статье будет общее описание библиотеки и необходимые ссылки и файлы. Многабукаф оставим графоманам, я же уже по традиции поясню все детали в приложенном видео, а текстом постараюсь передать только суть.
Поскольку это "решение с решениями" на все случаи - библиотека использует разные сторонние .dll, среди которых Leaf.xNet.dll, Nethereum, Otp.NET.dll, NBitcoin.dll, Npgsql.dll и целый пак других. Все необходимое последних совместимых c .Net 4.6 версий
};
namespace w3tools {
public static class FunctionStorage
что делает: Используется для хранения функций
зачем: Вам нужна быстровызываемая функция, но по какой-то причине вы не хотите, чтобы ее реализация присутствовала в общем коде. Объявляем через сниппет в объект-хранилище - вызываем из любого места по надобности.
#region SAFU
используется для работы с приватными данными (например расшифровывает приватные ключи из базы при подписи транзакций на лету)
Представляет из себя интерфейс для работы как раз с функциями из словаря: если у вас какой-то свой алгоритм генерации ключа шифрования - объявляете его сниппетом и тогда используется он. не объявляете - инициализируются функции заглушки, которые будут использовать входной пароль как AES-ключ.
#region Loggers
Разнообразные логгеры и дебаггеры с выбросом и нет.
Например W3Log автоматом покажет какой аккаунт сейчас работает в каком порту (а то по превью не всегда понятно в многопотоке откуда логи растут), сколько времени длится сессия и даже из какого метода был вызван лог, попутно установив значение в дебаг переменную или же считав из нее то, что нужно показать.
#region OnStart
создает необходимые переменные, задает базовые пути, читает состояние глобальных переменных или задает их, читает настройки всяких API из базы, устанавливает прокси и делает еще много всякого, что требуется для ровного старта и безопасного полета. Даже проверяет свое состояние по https://browserscan.net/
Также отключает логи Zenno .Ты же знаешь, мой юный друг, что логи - это не консоль! и все что ты в них вывел, остается на твоем диске, и в задачи Zenno не входит забота о безопасности твоих данных.
Да, да, даже тот самый приватный ключ, который ты решил посмотреть в логе для отладки один единственный раз, сейчас хранится на твоем диске в открытом виде.
Даже если ты не будешь использовать эту библиотеку - срочно делай
Код:
rd /s /q "<logs_path>" 2>nul & mklink /d "<logs_path>" "NUL"
#region SQLrd /s /q "C:\Program Files\ZennoLab\RU\ZennoPoster Pro V7\7.7.21.0\Progs\Logs" 2>nul & mklink /d "C:\Program Files\ZennoLab\RU\ZennoPoster Pro V7\7.7.21.0\Progs\Logs" "NUL"
Содержит методы для доступа к БД SQLite и Postgres (выбирает нужный в зависимости от входящих настроек) - для удобного создания и редактирования таблиц, ленивого доступа к данным аккаунта и универсального метода для любых видов запросов (W3Query).
#region POST/GET
Содержит упрощенные методы для работы с библиотекой Leaf и стандартных запросов средствами Zennoposter.
А также готовые методы для быстрого получения данных от RPC любого EVM based чейна в том формате, что вам удобен.
Метод:
Вызов:
C#:
public static T native<T>(IZennoPosterProjectModel project, string chainRPC = "", string address = "", string proxy = "",bool log = false)
{
if (address == "") address = project.Variables["addressEvm"].Value;
if (chainRPC == "") chainRPC = project.Variables["blockchainRPC"].Value;
string jsonBody = $@"{{ ""jsonrpc"": ""2.0"", ""method"": ""eth_getBalance"", ""params"": [""{address}"", ""latest""], ""id"": 1 }}";
string response;
using (var request = new HttpRequest())
{
request.UserAgent = "Mozilla/5.0";
request.IgnoreProtocolErrors = true;
request.ConnectTimeout = 5000;
if (!string.IsNullOrEmpty(proxy))
{
try
{
request.Proxy = ProxyClient.Parse(proxy.Contains("@") ? proxy : $"HTTP://{proxy}");
}
catch (Exception ex)
{
project.SendErrorToLog($"can't parse proxy '{proxy}': {ex.Message}");
throw;
}
}
try
{
HttpResponse httpResponse = request.Post(chainRPC, jsonBody, "application/json");
response = httpResponse.ToString();
}
catch (HttpException ex)
{
project.SendErrorToLog($"Err HTTPreq: {ex.Message}, Status: {ex.Status}");
throw;
}
}
var match = Regex.Match(response, @"""result""\s*:\s*""([^""]+)""");
string hexResultBalance = match.Success ? match.Groups[1].Value.TrimStart('0', 'x') : "0";
BigInteger balanceWei = BigInteger.Parse("0" + hexResultBalance, NumberStyles.AllowHexSpecifier);
decimal balanceNative = (decimal)balanceWei / 1000000000000000000m;
project.SendInfoToLog($"[Leaf.xNet] {address}: {balanceNative}");
if (typeof(T) == typeof(string)) return (T)Convert.ChangeType(balanceNative.ToString("0.##################", CultureInfo.InvariantCulture), typeof(T));
return (T)Convert.ChangeType(balanceNative, typeof(T));
}
C#:
var decimalBalance = native<decimal>(project);
#region Socials
В классе Socials собраны методы для работы с социалками. Проверки текущих статусов, логина по токену или логину/паролю в твиттер и примерно то же для Google
Метод:
Вызов:
C#:
public static void TwitterTokenSet(this Instance instance, IZennoPosterProjectModel project, string authToken = "", bool log = false, [CallerMemberName] string caller = "")
{
if (project.Variables["debug"].Value == "True") log = true;
instance.ClearCookie("x.com");
instance.ClearCookie("twitter.com");
instance.ClearCache("x.com");
instance.ClearCache("twitter.com");
if (authToken == "") authToken = project.Variables["twitterTOKEN"].Value;
string[] headers = new string[]
{
$"accept: {project.Profile.HTTPAccept}",
"accept-encoding: gzip, deflate, br",
$"accept-language: {project.Profile.AcceptLanguage}",
"sec-ch-ua-mobile: ?0",
"sec-ch-ua-platform: \"Windows\"",
"sec-fetch-dest: document",
"sec-fetch-mode: navigate",
"sec-fetch-site: none",
"sec-fetch-user: ?1",
"upgrade-insecure-requests: 1",
$"user-agent: {project.Profile.UserAgent}"
};
string result = ZennoPoster.HttpGet(
"https://x.com/",
instance.GetProxy(),
"UTF-8",
ZennoLab.InterfacesLibrary.Enums.Http.ResponceType.HeaderAndBody,
5000,
"",
project.Profile.UserAgent,
true,
5,
headers,
"",
false
);
instance.SetCookie($@".twitter.com TRUE / FALSE 05/18/2033 06:33:20 auth_token {authToken} FALSE TRUE");
instance.SetCookie($@".x.com TRUE / FALSE 05/18/2033 06:33:20 auth_token {authToken} FALSE TRUE");
if (log) Loggers.W3Log(project,$"[{caller}].[TwitterTokenSet] {authToken} set");
}
C#:
instance.TwitterTokenSet(project);
#region Time
Содержит методы для работы со временем, например
UnixNow() - вернет текущее UNIX время, а
cd() поможет выставить в базу cooldown в зависимости от вводных данных - число воспримется как минуты, строка как таймштамп вида "HH:mm:ss", а пустой ввод посчитает время до следующей полночи по UTC независимо от вашего часового пояса.
#region TX
дублирует методы получения информации от RPC, но с помощью стандартных запросов zenno
+ содержит методы для отправки Legacy и 1559 транзакций с авторасчетом газа с возможностью ускорения в процентах, логгированием и прочим
+ содержит универсальные методы для вызовов контрактов, которые не требуют знания ABI, а так же готовый метод рефуела через GazZip с автоподбором чейна с доступным балансом
#region CEX
Методы для работы с биржей Binance - от CEX (быстрого вывода на нужный нам некастодиальный аккаунт), ну и запроса баланса и истории орераций соответственно. Не нужно вводить API-ключи, генерировать подписи и прочее. При нехватке баланса вписываем 1 строку и радуемся
C#:
BinanceWithdraw(project,"0.1","ARBITRUM","ETH");
Методы для упрощенной работы с кошельками MetaMask и Keplr
Например метод instance.MMConfirm2(project); подтвердит любое действие в метамаске с обработкой разнообразных ситуаций и логгированием происходящего
Метод:
Вызов:
C#:
public static void MMConfirm2(this Instance instance,IZennoPosterProjectModel project, bool log = false )
{
instance.UseFullMouseEmulation = false;
DateTime urlChangeDeadline = DateTime.Now.AddSeconds(60);
int attemptCount = 0;
if (log)project.SendInfoToLog("Waiting for MetaMask URL to appear...");
while (!instance.ActiveTab.URL.Contains("nkbihfbeogaeaoehlefnkodbefgpgknn"))
{
Thread.Sleep(1000); attemptCount++;
if (log)project.SendInfoToLog($"Attempt {attemptCount}: Current URL is {instance.ActiveTab.URL}");
if (attemptCount > 5)
{
if (log)project.SendErrorToLog("Failed to load MetaMask URL within 6 seconds");
throw new Exception("Timeout waiting for MetaMask URL");
}
}
if (log)project.SendInfoToLog($"{instance.ActiveTab.URL} detected, pausing for 2 seconds...");
Thread.Sleep(2000);
HtmlElement allert = instance.ActiveTab.FindElementByAttribute("div", "class", "mm-box\\ mm-banner-base\\ mm-banner-alert\\ mm-banner-alert--severity-danger", "regexp", 0);
HtmlElement simulation = instance.ActiveTab.FindElementByAttribute("div", "data-testid", "simulation-details-layout", "regexp", 0);
HtmlElement detail = instance.ActiveTab.FindElementByAttribute("div", "class", "transaction-detail", "regexp", 0);
if (log)project.SendInfoToLog($"{Regex.Replace(simulation.GetAttribute("innertext").Trim(), @"\s+", " ")}");
if (log)project.SendInfoToLog($"{Regex.Replace(detail.GetAttribute("innertext").Trim(), @"\s+", " ")}");
if (!allert.IsVoid)
{
var error = Regex.Replace(allert.GetAttribute("innertext").Trim(), @"\s+", " ");
while (!instance.ActiveTab.FindElementByAttribute("button", "data-testid", "page-container-footer-cancel", "regexp", 0).IsVoid)
{
instance.ActiveTab.Touch.SwipeBetween(600, 400, 600, 300);
instance.WaitClick(() => instance.ActiveTab.FindElementByAttribute("button", "data-testid", "page-container-footer-cancel", "regexp", 0));
}
Loggers.W3Throw(project,error);
}
if (log)project.SendInfoToLog("Starting button click loop on MetaMask page...");
while (instance.ActiveTab.URL.Contains("nkbihfbeogaeaoehlefnkodbefgpgknn"))
{
if (DateTime.Now > urlChangeDeadline)
{
if (log)project.SendErrorToLog("Operation timed out after 60 seconds");
throw new Exception("Timeout exceeded while interacting with MetaMask");
}
try
{
if (log)project.SendInfoToLog("Attempting to find and click the confirm button...");
instance.WaitClick(() => instance.ActiveTab.FindElementByAttribute("button", "class", "button btn--rounded btn-primary", "regexp", 0), 3);
if (log)project.SendInfoToLog("Button clicked successfully");
Thread.Sleep(2000);
}
catch (Exception ex)
{
if (log)project.SendWarningToLog($"Failed to click button: {ex.Message}");
}
}
if (log)project.SendInfoToLog("MetaMask interaction completed, URL has changed");
instance.UseFullMouseEmulation = true;
}
C#:
instance.MMConfirm2(project);
#region OTP
Все еще получаете 2FA при помощи GET к некому API? Тогда мы идем к вам! Содержит метод для оффлайн генерации 2FA с проверкой на дедлайн, а так же методы для парсинга 6-значных кодов из почты, если ваша почта перенаправленна в FirstMail или Telegram.
#region Text&Vars
Методы для ленивых - например MathVar совершит некую математическую операцию с целым и переменной проекта провернув все конвертации под капотом. Например
MathVar(project,"i",-1);
вместоproject.Variables["i"].Value = (int.Parse(project.Variables["i"].Value) - 1).ToString();
как-то поприятнее и выглядит и пишется.
А
MultiplyEmail([email protected],4);
засибилит ваш gmail на 4 (создаст 4 псевдонима на которые можно ловить письма в родительский ящик).#region Browser
Набор расширенных методов для повседневных задач:
Wait&...
Зачем это нужно: такую конструкцию, как конвертирует Zennoposter
HtmlElement he = instance.ActiveTab.FindElementByAttribute("a", "data-testid", "AppTabBar_Profile_Link", "regexp", 0);
нельзя использовать например для ожидания, потому что элемент задается по поиску в момент объявления, и если он IsVoid, то и после появления элемента значение he не поменяется (если его не обновить с помощью параметров поиска снова конечно) что делает само объявление при ожидании бесполезным - поэтому берем сконвертированое значение поиска и используем в своих целях.примерами методов для такого использования являются WaitClick, ClickOut, WaitSetValue, WaitGetValue
Например:
WaitClick - примет параметры поиска элемента сконвертированные из кубика, будет ждать элемент не более чем {maxWaitSeconds}, после появления подождет {delay} секунд перед кликом
C#:
//пример клика
instance.WaitClick(() =>
instance.ActiveTab.FindElementByAttribute("a", "data-testid", "AppTabBar_Profile_Link", "regexp", 0));
Так же в этом регионе вы найдете методы для обхода CloudFlare бокса
instance.CFv2();
, закрытия лишних вкладок instance.CloseExtraTabs();
, однострочной операции instance.CtrlV("textToPaste");
, удаления элементов и декодирования Qr-кодов на лету.#region Nethereum / AES
регионы для работы с AES шифрованием и библиотекой Nethereum. Функционал этих регионов абстрагируется с помощью кода выше. В увлекательное путешествие по углубленному изучению принципов работы рекомендую взять с собой AI.
};
outro {
Часть функционала я продемонстрировал и прокомментировал в видео - обязательно досматриваем до конца
К статье прилагается шаблон использованный для демонстрации и актуальная на момент написания статьи версия w3tools.cs.
.cs файлы прикреплять нельзя - поэтому переименуйте расширение файла в .cs
Библиотека развивается и дополняется. Свежую версию всегда можно найти в репозитории GitHub
};
UPD: К моменту публикации в коде уже довольно много апдейтов, прилагаю акутальную версию init совместимого с GitHub.
UPD2: В GitHub репозитории добавленны методы для удобного взаимодействия с ShadowRoot
пример использования (просто подставляем нужный js селектор)
Код:
instance.JsSet("document.querySelector('body > initia-onboard').shadowRoot.querySelector('#quantity')",amount);
instance.JsClick("document.querySelector(\"body > initia-onboard\").shadowRoot.querySelector(\"div > main > div > footer > button\")");
Вложения
-
3,8 МБ Просмотры: 27
-
60,5 КБ Просмотры: 25
-
207,8 КБ Просмотры: 26
-
60,3 КБ Просмотры: 21
Последнее редактирование: