w3tools: Пиши шаблоны на C# с комфортом. 5000строк общего кода которые сохранят 5000 часов твоей жизни

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
131655


intro {
Всем привет, господа автоматизаторы:bt:
Это моя третья работа и, наверное, некий самоотчет по прогрессу.
Лирический дисклеймер
В качестве вводных отмечу, что в материал вложены полтора года непрерывного опыта разработки в ZP, начатого с околонулевым пониманием как программирования, так и автоматизации в целом.

Исходя из личного опыта (у вас он может быть другой) - могу сказать, что нет единого наилучшего формата обучения, который бы заходил всем. Кому-то нужно понять концепции и тогда можно как-то обработать объекты для обучения, а кому-то наоборот, любая концепция - туман, пока она не материализовалась в прикладную задачу из его собственной, а не абстрактной реальности.

Начинал я с кубиков, постепенно внедряя по необходимости решения на C# от AI или из чужих шаблонов. Также по необходимости я модифицировал эти решения, попутно разбираясь, как работает та или иная область в реальной ситуации, которая возникла не как самоцель обучения, а как реальный кейс, который мне нужно уметь обрабатывать в свете специфики именно моей деятельности, а не, к примеру, рассылок, которыми я не занимаюсь.
Да, про ООП мне все же пришлось покурить теоретический материал - но в целом процентов 90 знаний были получены и переварены в работе на разборе уже работающего в данный момент кода.

// TL;DR

Понимание принципов работы C# не необходимо для работы с готовым кодом С#, и если ты не "шаришь" в коде не спеши закрывать статью. Возможно именно использование функционала отсюда и прояснит для тебя некоторые вещи (также помним, что не пользоваться AI в наше время - все равно что хранить пульт от телевизора в пакете с пупырками, так даже бабули уже не все делают)

К делу!
В статье будет общее описание библиотеки и необходимые ссылки и файлы. Многабукаф оставим графоманам, я же уже по традиции поясню все детали в приложенном видео, а текстом постараюсь передать только суть.

Поскольку это "решение с решениями" на все случаи - библиотека использует разные сторонние .dll, среди которых Leaf.xNet.dll, Nethereum, Otp.NET.dll, NBitcoin.dll, Npgsql.dll и целый пак других. Все необходимое последних совместимых c .Net 4.6 версий (Когда там уже ZP8? - консольные приложения поднадоело писать в качестве костылей) прилагается в архиве со статьей

};

namespace w3tools {

public static class FunctionStorage
что делает: Используется для хранения функций
зачем: Вам нужна быстровызываемая функция, но по какой-то причине вы не хотите, чтобы ее реализация присутствовала в общем коде. Объявляем через сниппет в объект-хранилище - вызываем из любого места по надобности.

#region SAFU
используется для работы с приватными данными (например расшифровывает приватные ключи из базы при подписи транзакций на лету)
Представляет из себя интерфейс для работы как раз с функциями из словаря: если у вас какой-то свой алгоритм генерации ключа шифрования - объявляете его сниппетом и тогда используется он. не объявляете - инициализируются функции заглушки, которые будут использовать входной пароль как AES-ключ.

#region Loggers
131578

Разнообразные логгеры и дебаггеры с выбросом и нет.
Например W3Log автоматом покажет какой аккаунт сейчас работает в каком порту (а то по превью не всегда понятно в многопотоке откуда логи растут), сколько времени длится сессия и даже из какого метода был вызван лог, попутно установив значение в дебаг переменную или же считав из нее то, что нужно показать.


#region OnStart
131577

создает необходимые переменные, задает базовые пути, читает состояние глобальных переменных или задает их, читает настройки всяких API из базы, устанавливает прокси и делает еще много всякого, что требуется для ровного старта и безопасного полета. Даже проверяет свое состояние по https://browserscan.net/

Также отключает логи Zenno .Ты же знаешь, мой юный друг, что логи - это не консоль! и все что ты в них вывел, остается на твоем диске, и в задачи Zenno не входит забота о безопасности твоих данных.
Да, да, даже тот самый приватный ключ, который ты решил посмотреть в логе для отладки один единственный раз, сейчас хранится на твоем диске в открытом виде.
Даже если ты не будешь использовать эту библиотеку - срочно делай
Код:
rd /s /q "<logs_path>" 2>nul & mklink /d "<logs_path>" "NUL"
rd /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"
#region SQL
131608

Содержит методы для доступа к БД SQLite и Postgres (выбирает нужный в зависимости от входящих настроек) - для удобного создания и редактирования таблиц, ленивого доступа к данным аккаунта и универсального метода для любых видов запросов (W3Query).

#region POST/GET
131609

Содержит упрощенные методы для работы с библиотекой 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
131610

В классе 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
131612

Содержит методы для работы со временем, например
UnixNow() - вернет текущее UNIX время, а
cd() поможет выставить в базу cooldown в зависимости от вводных данных - число воспримется как минуты, строка как таймштамп вида "HH:mm:ss", а пустой ввод посчитает время до следующей полночи по UTC независимо от вашего часового пояса.

#region TX
131615

дублирует методы получения информации от RPC, но с помощью стандартных запросов zenno
+ содержит методы для отправки Legacy и 1559 транзакций с авторасчетом газа с возможностью ускорения в процентах, логгированием и прочим
+ содержит универсальные методы для вызовов контрактов, которые не требуют знания ABI, а так же готовый метод рефуела через GazZip с автоподбором чейна с доступным балансом

#region CEX
131616

Методы для работы с биржей Binance - от CEX (быстрого вывода на нужный нам некастодиальный аккаунт), ну и запроса баланса и истории орераций соответственно. Не нужно вводить API-ключи, генерировать подписи и прочее. При нехватке баланса вписываем 1 строку и радуемся
C#:
BinanceWithdraw(project,"0.1","ARBITRUM","ETH");
#region Wallets
131618

Методы для упрощенной работы с кошельками 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
131619

Все еще получаете 2FA при помощи GET к некому API? Тогда мы идем к вам! Содержит метод для оффлайн генерации 2FA с проверкой на дедлайн, а так же методы для парсинга 6-значных кодов из почты, если ваша почта перенаправленна в FirstMail или Telegram.

#region Text&Vars
131620

Методы для ленивых - например 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
131625

Набор расширенных методов для повседневных задач:
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));
Все методы из этого региона принимают динамические параметры поиска генерируемые Zenno, а не объект HtmlElement.
Так же в этом регионе вы найдете методы для обхода CloudFlare бокса instance.CFv2();, закрытия лишних вкладок instance.CloseExtraTabs();, однострочной операции instance.CtrlV("textToPaste");, удаления элементов и декодирования Qr-кодов на лету.

#region Nethereum / AES
131627

регионы для работы с 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\")");
132136
 

Вложения

Последнее редактирование:

Alex91

Известная личность
Read only
Регистрация
15.08.2024
Сообщения
880
Благодарностей
251
Баллы
63
Было бы хорошо, если бы не одна фигня...
А именно, то что в Зеннопостере обычно используют многопоток...
А использование конструкции static абсолютно в каждой функции, с порога не располагает к бездумному использованию...
 

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
Было бы хорошо, если бы не одна фигня...
А именно, то что в Зеннопостере обычно используют многопоток...
А использование конструкции static абсолютно в каждой функции, с порога не располагает к бездумному использованию...
Действительно ли все операции требуют многопоточного исполнения? Конкурирующие операции завернуты в lock, в обратном подходе не вижу особого смысла.
не располагает к бездумному использованию...
Ну это вообще прекрасно, к "бездумному использованию" я никого не призываю - совершенно напротив ;-)
 
Последнее редактирование:

Alex91

Известная личность
Read only
Регистрация
15.08.2024
Сообщения
880
Благодарностей
251
Баллы
63
Действительно ли все операции требуют многопоточного исполнения? Конкурирующие операции завернуты в lock, в обратном подходе не вижу особого смысла.

Ну это вообще прекрасно, к "бездумному использованию" я никого не призываю - совершенно напротив ;-)
Конечно все... Если нет желания, при запуске проекта на максимальных потоках, вылавливать фантомные нелогические потери данных.

Но вообще, каждый должен сам пройти путь граблей ;-)
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 860
Благодарностей
2 573
Баллы
113
Спасибо за материал!

Не знаю, что на счёт удобности использования самого решения в том виде, в котором оно существует...
Но, как минимум почитать код, найти нужные места, которые решают нужную задачу - подойдет всем однозначно!
 
  • Спасибо
Реакции: AmigoHarlamov и web3grep

DzAI

Client
Регистрация
25.03.2025
Сообщения
1
Благодарностей
0
Баллы
1
спасибо, золотой материал, для веб3
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 860
Благодарностей
2 573
Баллы
113

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 885
Благодарностей
1 628
Баллы
113
Уже 3 раза перечитал не понял, что это? для чего это? Вот я разработчик чем мне это поможет? Такое чувство что соединили кучу библиотек в 1. Не пойму зачем. Нечего против статьи не имею, ТС молодец, просто я не могу понять. Вот хоть убей не пойму я зачем это надо и какая с этого польза.

теперь понял в общих чертах что это и для чего

132088
 
Последнее редактирование:
  • Спасибо
Реакции: wizard и web3grep

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
Уже 3 раза перечитал не понял, что это? для чего это? Вот я разработчик чем мне это поможет? Такое чувство что соединили кучу библиотек в 1. Не пойму зачем. Нечего против статьи не имею, ТС молодец, просто я не могу понять. Вот хоть убей не пойму я зачем это надо и какая с этого польза.

теперь понял в общих чертах что это и для чего

Посмотреть вложение 132088
огрокано во всей полноте )
 

Sherminator

Client
Регистрация
10.09.2021
Сообщения
1 441
Благодарностей
821
Баллы
113
.
 
Последнее редактирование:

OlegR

Client
Регистрация
11.06.2015
Сообщения
247
Благодарностей
253
Баллы
63
минимализм рулит
 
  • Спасибо
Реакции: web3grep

kul0n

Client
Регистрация
10.03.2016
Сообщения
96
Благодарностей
31
Баллы
18
И не только для веб3, но и в целом для изучения
действительно, применять можно не только в веб3, сам пользуюсь заготовками автора нескоько мес. - очень упрощает работу + паралельно подтягиваются скилы по C#
 
  • Спасибо
Реакции: web3grep

maestroant

Client
Регистрация
28.01.2023
Сообщения
7
Благодарностей
2
Баллы
3
А использование конструкции static абсолютно в каждой функции, с порога не располагает к бездумному использованию...
Ну нет же, откуда вы взяли что нельзя использовать статичные методы в много потоке?))
 
  • Спасибо
Реакции: web3grep

wizard

Client
Регистрация
04.10.2011
Сообщения
1 021
Благодарностей
651
Баллы
113
мне тоже не понятно
Уже 3 раза перечитал не понял, что это? для чего это? Вот я разработчик чем мне это поможет? Такое чувство что соединили кучу библиотек в 1. Не пойму зачем. Нечего против статьи не имею, ТС молодец, просто я не могу понять. Вот хоть убей не пойму я зачем это надо и какая с этого польза.
я тоже не вижу особой пользы. И не понимаю что тут увидели другие,
наверняка раз столько людей проголосовало - то это точно что-то стоящее.
может я глуп? :(

P.S. Нечего против статьи не имею, ТС молодец.
 
  • Спасибо
Реакции: samsonnn

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 885
Благодарностей
1 628
Баллы
113
да, и я тоже :(

У ТС инновационная разработка, без которой сейчас не как вообще. Зачем для каждого проекта подключать только нужные библиотеки? Проще уже все библиотеки интернета в одну запихнуть и таскать с собой. Как по мне, то очень плохой пример для новичков, и это не правильно, но им об этом некто не сказал, потом и кодеры такие получаются. Ну каждому свое. Мне проще подключить 5 нужных библиотек dll, чем одну огромную не пойми что, и потом там ищи, что от куда и куда, а про обновление всего этого я вообще молчу. Раз народу зашло, значит для них полезно! Пользуйтесь на здоровье.
 
Последнее редактирование:

Sherminator

Client
Регистрация
10.09.2021
Сообщения
1 441
Благодарностей
821
Баллы
113
Всё пытался удержаться, но всё же скажу, тоже не понимаю что это за стена кода из за который народ в ладоши так лопает. Видимо я тоже глуп
 
  • Спасибо
Реакции: samsonnn

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
да, и я тоже :(

У ТС инновационная разработка, без которой сейчас не как вообще. Зачем для каждого проекта подключать только нужные библиотеки? Проще уже все библиотеки интернета в одну запихнуть и таскать с собой. Как по мне, то очень плохой пример для новичков, и это не правильно, но им об этом некто не сказал, потом и кодеры такие получаются. Ну каждому свое. Мне проще подключить 5 нужных библиотек dll, чем одну огромную не пойми что, и потом там ищи, что от куда и куда, а про обновление всего этого я вообще молчу. Раз народу зашло, значит для них полезно! Пользуйтесь на здоровье.
Затем, что проекты могут писаться командой людей. Каждый член команды может запустить шаблон другого участника, не подстраивая под него набор библиотек и общий код. Все могут использовать одинаковые вызовы, не заботясь об обновлении общего кода в самом шаблоне (поскольку он подтягивается из файла). Это делает работу нескольких человек модульной и более удобной, чем сбор индивидуального набора под каждый проект. Возможно, специфика вашей деятельности не позволяет вам увидеть, какую пользу можно из этого извлечь.
А что касается многопоточности — я, пожалуй, обращусь к помошнику и приведу его цитату. Для примера я взял один кусок кода, но в целом смысл ответа должен показать, действительно ли это необходимо или все же нужно обратить внимание на нюансы.

Анализ кода
1. Параметры и локальные переменные
csharp
СвернутьПереносКопировать
public static void KeplrImportPkey(this Instance instance, IZennoPosterProjectModel project, bool temp = false) { var walletPassword = SAFU.HWPass(project); var key = new Key().ToHex(); var walletName = "temp"; if (!temp) key = Db.KeyEVM(project); if (!temp) walletName = "pkey"; // ... }
  • instance — это объект, передаваемый в метод. Он не статический, а принадлежит конкретному вызову.
  • project — тоже параметр, уникальный для каждого вызова.
  • walletPassword, key, walletName — локальные переменные, создаваемые в рамках метода. Они изолированы для каждого потока, так как каждый поток имеет свой стек.
Вывод: Локальные переменные и параметры сами по себе потокобезопасны, если не передаются между потоками.
2. Вызовы внешних методов
  • SAFU.HWPass(project): Возвращает пароль. Если этот метод чистый (не изменяет глобальное состояние) и зависит только от project, он безопасен. Но если он обращается к статическим данным или ресурсам (например, читает из файла или базы без синхронизации), это может быть проблемой.
  • new Key().ToHex(): Создаёт новый объект Key и вызывает его метод. Если Key не использует статические поля или общие ресурсы, это безопасно.
  • Db.KeyEVM(project): Предположительно, обращается к базе данных. Безопасность зависит от реализации Db:
    • Если Db.KeyEVM использует статическое подключение к базе без синхронизации, это может привести к конфликтам в многопотоке.
    • Если каждый вызов создаёт новое подключение или использует потокобезопасный доступ (например, через пул соединений), то всё в порядке.
3. Работа с instance
Метод активно использует объект instance для взаимодействия с браузером:
  • instance.WaitClick, instance.WaitSetValue, instance.WaitGetValue, instance.CloseExtraTabs — все эти методы работают с instance.ActiveTab, который, скорее всего, представляет вкладку браузера.
  • Пример:
    csharp
    СвернутьПереносКопировать
    instance.WaitClick(() => instance.ActiveTab.FindElementByAttribute("button", "innertext", "Import\\ an\\ existing\\ wallet", "regexp", 0));
Ключевая деталь:
  • instance передаётся как параметр, то есть каждый поток может работать со своим экземпляром Instance. Если это так, то каждый поток управляет своим браузером или вкладкой, и конфликтов не будет.
  • Однако, если все потоки используют один и тот же объект instance, то метод становится потоконебезопасным, потому что:
    • Одновременные вызовы FindElementByAttribute, RiseEvent("click") и других методов могут мешать друг другу.
    • Например, один поток ищет кнопку "Import", а другой в это же время кликает по другой кнопке — это приведёт к непредсказуемому поведению.
4. Цикл с Thread.Sleep
csharp
СвернутьПереносКопировать
while (!instance.ActiveTab.FindElementByAttribute("button", "innertext", "Import", "regexp", 0).IsVoid) { instance.ActiveTab.FindElementByAttribute("button", "innertext", "Import", "regexp", 0).RiseEvent("click", instance.EmulationLevel); Thread.Sleep(2000); }
  • Этот цикл ждёт, пока элемент станет недоступным, кликая по нему каждые 2 секунды.
  • Если несколько потоков работают с одним instance, они могут одновременно пытаться кликать по одному и тому же элементу, что вызовет хаос.
5. Отсутствие статических полей
В самом методе нет прямого обращения к статическим полям или общим ресурсам, что хорошо. Проблемы могут возникнуть только из-за внешних вызовов (SAFU, Db) или общего instance.
Потокобезопасен ли код?
  • Да, если каждый поток использует свой instance:
    • Если каждый поток работает с отдельным объектом Instance (например, каждый поток открывает свой браузер или вкладку), то метод потокобезопасен. Локальные переменные и действия внутри метода изолированы.
    • В этом случае вызовы SAFU.HWPass и Db.KeyEVM тоже должны быть независимыми (например, использовать разные подключения к базе).
  • Нет, если instance общий для всех потоков:
    • Если все потоки передают один и тот же объект Instance, то одновременные операции с ActiveTab (поиск элементов, клики, установка значений) будут конфликтовать. Например:
      • Поток 1 ищет кнопку "Import".
      • Поток 2 в это время кликает по другой кнопке или меняет состояние страницы.
      • Результат: ошибки, пропущенные клики или некорректное поведение.
  • Зависит от SAFU.HWPass и Db.KeyEVM:
    • Если эти методы используют статические ресурсы (например, одно подключение к базе или общий файл) без синхронизации, они могут добавить потоконебезопасность.
Как проверить?
Чтобы понять, безопасен ли код в твоём случае, нужно ответить на вопросы:
  1. Каждый поток создаёт свой instance, или они используют один общий?
  2. SAFU.HWPass и Db.KeyEVM — как они реализованы? Есть ли у них статические поля или общие ресурсы?
Пример проверки:
csharp
СвернутьПереносКопировать
var instance1 = new Instance(); // Для потока 1 var instance2 = new Instance(); // Для потока 2 Task.Run(() => instance1.KeplrImportPkey(project, false)); Task.Run(() => instance2.KeplrImportPkey(project, false));
  • Если instance1 и instance2 разные — безопасно.
  • Если они одинаковые — проблемы.
Как сделать потокобезопасным?
Если instance общий или внешние методы (SAFU, Db) небезопасны, вот как можно исправить:
  1. Гарантировать уникальность instance:
    • Убедись, что каждый поток создаёт свой объект Instance. Например:
      csharp
      СвернутьПереносКопировать
      Task.Run(() => { var instance = new Instance(); instance.KeplrImportPkey(project, false); });
  2. Синхронизация через lock:
    • Если instance общий, добавь блокировку:
      csharp
      СвернутьПереносКопировать
      private static readonly object LockObject = new object(); public static void KeplrImportPkey(this Instance instance, IZennoPosterProjectModel project, bool temp = false) { lock (LockObject) { // Весь код метода } }
      • Минус: потоки будут выполняться последовательно, что может замедлить работу.
  3. Проверка внешних методов:
    • Если SAFU.HWPass или Db.KeyEVM используют статические данные, добавь в них синхронизацию (например, lock или Interlocked).
Итог
  • Потокобезопасен: Если каждый поток работает со своим instance, и внешние методы (SAFU.HWPass, Db.KeyEVM) не используют общие ресурсы без синхронизации.
  • Потоконебезопасен: Если instance общий или внешние методы имеют состояния, доступные всем потокам.
 
  • Спасибо
Реакции: Hanger

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 885
Благодарностей
1 628
Баллы
113
Как я это вижу? У вас есть кухня и 10 поваров которые готовят одно блюдо. А по хорошему должно быть: 1 повар и 9 помощников которые будут помогать готовить блюдо, но управлять всем процессом будет 1 повар.
А у вас получается как лебедь, рак, и щука... Например сегодня писали проект, и он выглядел по одному, завтра приходим и он уже выглядит по другому, там какой то спец по своему все делал всю ночь, а через какое то время вообще по другому все выглядит.

В командной работе все совершенно иначе. Там берется ТЗ обсуждается, делится на всех, каждый отвечает за конкретный кусок. Потом эти куски собираются воедино и получается полный готовый проект. Но отвечает за весь проект главный разработчик, который проверяет то что написали. Ну как учитель в школе проверяет работы школьников. Та ладно что я тут распинаюсь, что то пытаюсь доказывать, оно мне надо? Делайте что хотите. Вам так нравится? Работайте.
 
  • Спасибо
Реакции: Dmitriy Ka и web3grep

kul0n

Client
Регистрация
10.03.2016
Сообщения
96
Благодарностей
31
Баллы
18
по-моему тут дело не в том что кто-то глуп, а кто-то умён, просто у всех разные подходы, сферы деятельности и разные кухни) соответственно и видит тут каждый своё.
 
  • Спасибо
Реакции: web3grep

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
Как я это вижу? У вас есть кухня и 10 поваров которые готовят одно блюдо. А по хорошему должно быть: 1 повар и 9 помощников которые будут помогать готовить блюдо, но управлять всем процессом будет 1 повар.
А у вас получается как лебедь, рак, и щука... Например сегодня писали проект, и он выглядел по одному, завтра приходим и он уже выглядит по другому, там какой то спец по своему все делал всю ночь, а через какое то время вообще по другому все выглядит.

В командной работе все совершенно иначе. Там берется ТЗ обсуждается, делится на всех, каждый отвечает за конкретный кусок. Потом эти куски собираются воедино и получается полный готовый проект. Но отвечает за весь проект главный разработчик, который проверяет то что написали. Ну как учитель в школе проверяет работы школьников. Та ладно что я тут распинаюсь, что то пытаюсь доказывать, оно мне надо? Делайте что хотите. Вам так нравится? Работайте.
У нас 10 шеф поваров которые готовят 10 разных блюд. Но используют для этого один и тот же инструментарий, покольку инструментарий в нашем случае - информация - то он свободно масштабируемый. И каждый шеф может взять инструмент с полки другого шефа, а потом поделиться своим блюдом.

Грубоговоря у вас есть венчик, а вам нужно замутить ведро теста: варианты - стереть руки кровь до плечей работатая венчиком, придумать и собрать миксер - (трудоемко, но будет миксер который работает как тебе нужно), воспользоваться миксером соседа у которого с большой вероятностью тот же тип розетки и напряжение в сети что и у тебя. Из чисто соседской благодарности можно угостить соседа пирогом который у тебя вышел.
Даже кухни бывают разные - потому что назначение у них разное (как и специфика деятельности у пользоветелей Zennoposter), и так уж сложилось что ТСу удалось получить экспириенс работы на них как в качестве помошника, так и в качестве шефа - и даже там все не так однозначно, как вы описали
 
  • Спасибо
Реакции: kul0n

wizard

Client
Регистрация
04.10.2011
Сообщения
1 021
Благодарностей
651
Баллы
113
по-моему тут дело не в том что кто-то глуп, а кто-то умён, просто у всех разные подходы, сферы деятельности и разные кухни) соответственно и видит тут каждый своё.
Вы не поняли - к автору нет претензий - у него своя дорога ) не мне рассказывать как по ней идти.
меня удивило что именно эта статья, по голосованию, идет впереди с большим отрывом :bn:
значит сообщество считает ее самой полезной и\или интересной - а я НЕ ПОНИМАЮ почему )
по этому я и предположил что возможно я не понимаю всю глубину глубин
ну да ладно ... есть как есть
P.S. Заранее извиняюсь если кого обидел, не было такой цели
 
  • Спасибо
Реакции: samsonnn

volody00

Client
Регистрация
06.09.2016
Сообщения
970
Благодарностей
1 065
Баллы
93
Всё пытался удержаться, но всё же скажу, тоже не понимаю что это за стена кода из за который народ в ладоши так лопает. Видимо я тоже глуп
Это вполне может быть мультиаккаунтинг. Голосуют сейчас мало. Голосование анонимное. Для статуса клиента достаточно потратить 3000 руб. Я и в прошлый раз удивился, что он занял второе место. Теперь такая же статья и опять в топ 3. За второе место дают + $300. Окупается за 1-2 конкурса.

К слову, количество просмотров его шаблона меньше чем количество проголосовавших.

Заранее прошу прощения, что качу бочку без доказательств. Спасибо всем, кто принимает участие в конкурсах
 
  • Спасибо
Реакции: Sherminator

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
Это вполне может быть мультиаккаунтинг. Голосуют сейчас мало. Голосование анонимное. Для статуса клиента достаточно потратить 3000 руб. Я и в прошлый раз удивился, что он занял второе место. Теперь такая же статья и опять в топ 3. За второе место дают + $300. Окупается за 1-2 конкурса.

К слову, количество просмотров его шаблона меньше чем количество проголосовавших.

Заранее прошу прощения, что качу бочку без доказательств. Спасибо всем, кто принимает участие в конкурсах
Дело в том что есть достаточное колличество людей которые пользуются этими наработками, и чтобы их оценить им не нужно ждать ни статьи, ни шаблона. По сути они уже знают за что они голосуют.
К тому же, смею предположить, что голоса дают в основном те, чьи проблемы решают мои работы в том или ином ракурсе. А чтобы решать какую-то проблему она должна быть, а чтобы решать ее много - она должна быть популярной. Так вот думаю основная ценность в том что мои работы решают большое колличество простых вопросов возникающих в процессе у любого на пути. То есть все они собраны из набитых в работе шишек, и их не надо выбирать из форума по крупицам.

Да я вижу в каждом конкурсе действительно сложные, достойные внимания решения, и складываю их в закладки с мыслями "однажды точно пригодится", но по факту 90% из них не решают МОИХ ПРИКЛАДНЫХ задач.
Круто - да, сложно - да, мое уважение и почет - да, поможет ли мне это - нет.

Вот, думаю, и дело в том - что в каждой из работ закрыто куча вопросов а не какой-то один специалный ультрасложныйкейс.



Никого не хотел обидеть , все работы ценны - просто каждая будет больше востребована в той или иной нише. У каждой будет своя аудитория
 
Последнее редактирование:

volody00

Client
Регистрация
06.09.2016
Сообщения
970
Благодарностей
1 065
Баллы
93
@Sherminator , всё оказалось гораздо проще, чем я расписал - https://zenno.club/discussion/threads/zennolab-master-4-0-golosovanie-za-luchshie-raboty.126620/post-839289 . Раньше такое не разрешалось, сейчас хз. @web3grep , прости, просто была найдена аномалия и хотелось разобраться. У тебя хорошая работа. Спасибо, что принял участие в конкурсе
 

samsonnn

Client
Регистрация
02.06.2015
Сообщения
1 885
Благодарностей
1 628
Баллы
113
@Sherminator , всё оказалось гораздо проще, чем я расписал - https://zenno.club/discussion/threads/zennolab-master-4-0-golosovanie-za-luchshie-raboty.126620/post-839289 . Раньше такое не разрешалось, сейчас хз. @web3grep , прости, просто была найдена аномалия и хотелось разобраться. У тебя хорошая работа. Спасибо, что принял участие в конкурсе
@volody00 вы случайно не кибер-следователь? :ay:
 

volody00

Client
Регистрация
06.09.2016
Сообщения
970
Благодарностей
1 065
Баллы
93
@samsonnn, да я сам не знаю, чего я до него докапался. вообще не люблю такое. Ты ж до меня докапывался (да и я бывало на тебя нападал), неприятно. Но тут просто так совпало, что я в прошлый раз был как-то удивлен, почему работа меня особо не привлекавшая заняла 2 место и тут опять начался халевар непонятный, вот я и встрял. Много хороших работ, а эта впереди планеты всей. Скорее всего он по незнанию просто так сделал. В принципе, если бы он написал "стартовал конкурс и я тоже принял участие. Приходите и читайте" то этого разговора бы не было.
 

Sherminator

Client
Регистрация
10.09.2021
Сообщения
1 441
Благодарностей
821
Баллы
113
@Sherminator , всё оказалось гораздо проще, чем я расписал - https://zenno.club/discussion/threads/zennolab-master-4-0-golosovanie-za-luchshie-raboty.126620/post-839289 . Раньше такое не разрешалось, сейчас хз. @web3grep , прости, просто была найдена аномалия и хотелось разобраться. У тебя хорошая работа. Спасибо, что принял участие в конкурсе
Сгорел если честно...
 

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
если бы он написал "стартовал конкурс и я тоже принял участие. Приходите и читайте"
Это уже ваши личные тараканы вашего восприятия так реагируют на мой внешний ресурс, манера письма в котором никак к работе не относится.
 

Zedx

Client
Регистрация
12.06.2018
Сообщения
1 397
Благодарностей
955
Баллы
113
Почистить бы ещё код... Бегло глянул, заметил, что подключена библиотека QBitNinja, но она уже пару лет как не поддерживается и её api не работает.
 
  • Спасибо
Реакции: web3grep

web3grep

Client
Регистрация
27.11.2023
Сообщения
45
Благодарностей
96
Баллы
18
Почистить бы ещё код... Бегло глянул, заметил, что подключена библиотека QBitNinja, но она уже пару лет как не поддерживается и её api не работает.
Согласен , да и дублирующий код тоже есть. Отрефакторим.
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)