- Регистрация
- 10.09.2021
- Сообщения
- 1 261
- Благодарностей
- 679
- Баллы
- 113
Введение
Приветствую всех на 20-ом конкурсе статей форума ZennoLab!
В статье я поделюсь своим опытом в создании шаблонов на ZennoDroid, с учетом использования кубиков, C# и VisualStudio. Мой материал окажется полезным в первую очередь для тех кто только начинает знакомство с ZennoDroid. В статье я расскажу не только про технические аспекты, но и поделюсь собственными наработками.
Основы ZennoDroid для новичков с кубиками
В целом по кубикам отличий от зенопостера не так много, большая часть из них скопирована, появилась дополнительная вкладка под андроид, там и осуществляется основная работа с устройством.
Быстрый старт осуществляется с помощью 4х кубиков: Создаем устройство, выбираем его, проксируем, запускаем.
Создание, выбор и запуск находятся во вкладке “Действия с устройством”, проксирование во вкладке “Настройки устройства”.
В кубике с названием надо обязательно указать переменную для индекса создаваемого устройства (индекс генерируется автоматически), а в кубике выбора указать эту переменную что бы он по индексу выбрал нужное устройство.
Всё, машина запущена, можно начинать с ней работать.
Теперь немного нырнем в параметры устройства, вкладка “Настройки устройства”.
В целом, сам ZennoDroid генерирует вполне сносные параметры, лично я в обязательном порядке меняю разрешение экрана, потому что по умолчанию там планшет получается и докидываю ядер с оперативкой, что бы устройство пошустрее было. Все настройки которые вы тут задаете они сохраняются в профиль.
Что бы они применились, надо запускать устройство с параметром применения настроек профиля.
Теперь попробуем мальца изменить параметры .
Если с настройкой IMEI, Android ID все понятно, то с оператором нужно знать некоторые моменты, они прописаны в документации ZennoDroid.
Вот сайт с требуемыми данными https://www.mcc-mnc.com/
По поводу смены модели устройства, если с производителем всё понятно, то модель надо указывать (на пример для самсунга) не Galaxy s10+, а SM-G975X, иначе вы даже проверку устройства не пройдете при попытке авторизации в гугл плей.
Поля ro.product.brand и ro.product.board надо искать в файле build.prop для требуемой модели, файл этот находится в гугле за 5 минут.
Теперь после того как мы настроили устройство и запустили его, можно на него что-нибудь установить, для этого идем на вкладку “Действия с приложениями”. Установим яндекс браузер, для этого надо просто указать путь к апк файлу.
Вот мы накатили наше первое приложение, теперь его надо запустить, для этого выберем в этом же кубике пункт “Открыть приложение”, нам надо указать имя приложения, для того что бы узнать его имя мы идем на вкладку “Установленные приложения” и там находим наш яндекс браузер и копируем его название, затем подставляем его в кубик и пробуем запустить.
По такому же принципу (через название приложение) осуществляется работа с остальными опциями в кубике “Действия с приложениями”.
Управление через AdbShell
Ещё хотелось бы рассказать про Adb Shell команды которые упрощают некоторые действия. Кубик “Утилиты”, опция “Консольная команда”. Пробегусь по паре основных команд, в целом их создавать не так сложно, заходите в ChatGPT и он их очень ловко генерирует. Обязательно надо указывать переменную в которую положится результат выполнения кубика, а то он с ошибкой будет выполняться.
Команда которая открывает браузер по умолчанию и переходит на нужный нам URL.
adb shell am start -a android.intent.action.VIEW -d http://www.yahoo.com
Команда что бы открыть настройки приложения, в данном случая яндекс браузера, тут так же используется название приложения.
adb shell am start -a android.settings.APPLICATION_DETAILS_SETTINGS -d package:com.yandex.searchapp
Команда для открытия настроек разработчика.
adb shell am start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS
Команда что бы сделать скриншот.
adb shell screencap -p /storage/emulated/0/Download/screenshot.png
Скриншот сохраняется в папку Download (это основная папка для обмена файлами между эмулятором и пк) на пк это папка находится по пути C:\Users\UserName\Downloads\MEmu Download
В целом через adb shell команды очень удобно производить настройки самого устройства без создания лишних команд для тачей и свайпов.
Про свайпы и тачи будет дальше в статье, мне стандартное решение не нравится, предложу вам свое.
Подводные камни
За время работы с ZennoDroid заметил следующие неприятности.
1) Вес одной машины
Одно устройство весит довольно не мало 2 гб, чем больше вы с ним работаете тем больше оно будет весить. В итоге небольшая ферма на 500 устройств будет весить терабайт (как минимум).
2) Скорость работы устройства
В зависимости от сгенерированного устройства и используемых приложений, скорости интернета и мощности вашего пк будет зависеть успешность выполнения вашего шаблона, на пример, вы произвели запуск устройства, кубик завершился удачно, следующим у вас идет кубик запуска приложения, и он у вас выполняется не удачно, потому что устройство хоть и запустилось но оно не прогрузилось как следует, а ZennoDroid этот момент не ловит, в итоге вы приложение не запустите, так же произойдет ошибка если вы попробуете выполнить adb shell команду пока устройство не прогрузилось.
3) Создание большого количества машин
Если вы создаете в MEmu очень много машин, ну хотя бы 1000, у вас появится 2 очень неприятных проблемы. Первая, очень часто начнет вылетать ошибка при попытке создать новое устройство, иногда даже раз по 5 подряд. Вторая, после неудачной попытки создания, список устройств в Memu будет автоматически пролистывать до самого конца. Можете это увидеть вот тут https://zennolab.com/discussion/threads/prolistyvanie-spiska-ustrojstv.115103/
4) Одновременный запуск машин
Запуск большого количества потоков подтягивает проблему одновременного запуска машин, ресурсы на запуск как я понял делятся между машинами и чем больше вы машин одновременно запускаете, тем дольше будет производится запуск, тем больше шансов что шаблон упадет в ошибку из за таймаута на запуск машины.
А теперь расскажу вам как каждую из этих “Фичей” я решал.
1) По уменьшению веса машины я писал целую статью, можете с ней по ссылке ознакомиться. https://zennolab.com/discussion/threads/umenshenie-razmera-mashiny-zennodroid-dlja-xranenija.114499/
2) Тут всё конечно посложнее, универсального решения у меня нету, есть только советы, помнить о том что такая проблема есть и руками выставлять паузы перед действиями. У меня есть свой метод для ожидания элементов который частично решает проблему. Его я напишу в части про c#
3) Этот момент связан с первым пунктом, в той статье я допустил не большую ошибку (о которой я узнал позже), получается мы оставляли папку с машиной в мему и количество машин которые отображаются в memu постоянно росло. Сейчас делаю иначе. Алгоритм такой. Создаю новую машину, работаю с ней, потом файл disk2 перемещаю на хранение, а саму машину удаляю. Когда мне вновь надо поработать с этой машиной, я создаю новую машину, удаляю в ней файл disk2 и подкидываю туда свой disk2 который на хранение был, после работы убираю этот диск обратно на хранение а машину удаляю. В итоге у меня количество созданных эмуляторов в мему равно количеству потоков с которыми я работаю. Ну и ещё есть не большой момент. После обновления версии MEmu там меняются цифры в название файла disk2 и disk1, в итоге когда вы подкидываете свой файл disk2 надо будет ему сменить название на актуальные цифры которые используются в названии disk1.
4) Тут всё просто, если комп слабый, делаем лок, что бы у нас одновременно мог производить запуск только один эмулятор, если комп помощнее, делаем семафор и указываем сколько эмуляторов можно запускать одновременно. Этими двумя методами я с вами поделюсь в части про c#.
С#
Пробежимся по основным методам, находятся они в классе
instance.DroidInstance
В комментариях к методам разработчики решили себя не утруждать, поэтому догадываться придется самим. Я напишу основные, остальное думаю вам не составит труда по названиям определить, имена большей части методов присвоены адекватные.
C#:
instance.DroidInstance.Action – выбор запуск, создание и остановка устройства
instance.DroidInstance.App – действия с приложениями
instance.DroidInstance.AppiumDriver – получение объекта элемента
instance.DroidInstance.Input – свайпы, тачи, ввод текста
instance.DroidInstance.AppiumDriver.FindElementById();
- по атрибуту resource-id
instance.DroidInstance.AppiumDriver.FindElementByAccessibilityId()
;- по атрибуту content-desc
instance.DroidInstance.AppiumDriver.FindElementsByClassName()
; - по атрибуту class
С поиском через xPath думаю вопросов быть не должно.
C# Кубики
А теперь напишем несколько кубиков. Для свайпа, тача, ввода текста и ожидания элемента.
C#:
public IAndroidElementAPI WaitElementByXPath(string xPath)
{
IAndroidElementAPI element = null;
try
{
element = instance.DroidInstance.AppiumDriver.FindElementByXPath(xPath);
}
catch (Exception ex)
{
throw new Exception("Не удалось осуществить инициализацию элемента для ожидания, проверьте xPath: " + ex.Message);
}
int counter = 0;
while (element == null)
{
if (counter == 10) //Количество попыток для инициализации
{
throw new Exception("Элемент не появился: " + xPath);
}
project.SendInfoToLog("Ожидание элемента: " + xPath);
element = instance.DroidInstance.AppiumDriver.FindElementByXPath(xPath);
Thread.Sleep(3000); //Пауза между попытками в МС
counter++;
}
Thread.Sleep(1000); //Пауза после нахождения элемента в МС, нужна для того что иногда элемент находится в дереве быстрее того как он появится на экране.
return element; //Возвращаем инициализированный элемент.
}
C#:
public void ClickToElementByXpath(string xPath, int PauseAfterClickInSec) // В качестве параметров принимает xPath и паузу после клика в секундах
{
Random rnd = new Random();
IAndroidElementAPI Element = WaitElementByXPath(xPath); //Принимаем элемент из прошлого метода
var elementLocation = Element.GetAttribute("bounds").Replace("][", "|").Replace("]", "").Replace("[", ""); //Получаем его координаты из свойства bounds
//Раскладываем по переменным его координаты
int x1 = Convert.ToInt32(elementLocation.Split('|')[0].Split(',')[0]);
int x2 = Convert.ToInt32(elementLocation.Split('|')[1].Split(',')[0]);
int y1 = Convert.ToInt32(elementLocation.Split('|')[0].Split(',')[1]);
int y2 = Convert.ToInt32(elementLocation.Split('|')[1].Split(',')[1]);
int randomX = rnd.Next(x1,x2); // Получаем рандомную точку между координатами x
int randomY = rnd.Next(y1, y2); // Получаем рандомную точку между координатами y
instance.DroidInstance.Input.Tap (randomX, randomY); //деламем клик
project.SendInfoToLog("Сделали клик по элементу: " + xPath, true);
Thread.Sleep(PauseAfterClickInSec * 1000);
}
C#:
public void SwipeUp()
{
Random random = new Random();
int height = Convert.ToInt32(Convert.ToDouble(project.Profile.DisplayHeight) * 0.87); //получаем и уменьшаем высоту экрана на 13% что бы не цеплять элементы интерфейса
int width = project.Profile.DisplayWidth; // получаем ширину экрана
int randomHeight = (height * random.Next(30, 40)) / 100; //Определяем размер поля для свайпа по y
int randomWidth = (width * random.Next(10, 17)) / 100; Определяем размер поля для свайпа по x
instance.DroidInstance.Input.Swipe
(random.Next(width / 2 - randomWidth, width / 2 + randomWidth), random.Next(height / 2 + randomHeight, height / 2 + randomHeight),
random.Next(width / 2 - randomWidth, width / 2 + randomWidth), random.Next(height / 2 - randomHeight, height / 2 - randomHeight),
random.Next(1000, 2000));
}
C#:
public void SwipeDown()
{
Random random = new Random();
int height = Convert.ToInt32(Convert.ToDouble(project.Profile.DisplayHeight) * 0.87);
int width = project.Profile.DisplayWidth;
int randomHeight = (height * random.Next(30, 40)) / 100;
int randomwidth = (width * random.Next(10, 17)) / 100;
instance.DroidInstance.Input.Swipe
(random.Next(width / 2 - randomwidth, width / 2 + randomwidth), random.Next(height / 2 - randomHeight, height / 2 - randomHeight),
random.Next(width / 2 - randomwidth, width / 2 + randomwidth), random.Next(height / 2 + randomHeight, height / 2 + randomHeight), random.Next(1000, 2000));
}
C#:
public void SwipeSearchElementByXpath(string xpath)
{
var element = instance.DroidInstance.AppiumDriver.FindElementByXPath(xpath); // инициализируем элемент
Random random = new Random();
int counter = 0;
while (element == null) //Проверяем наличие элемента, если его нету то заходим в while
{
if (counter == 20) //Количество попыток свайпа
{
throw new Exception("Сделали 20 свайпов а элемент так и не появился");
}
SwipeUp(); // Делаем свайп
element = instance.DroidInstance.AppiumDriver.FindElementByXPath(xpath); // Пробуем найти элемент
Thread.Sleep(random.Next(500, 1000)); //Пауза между свайпами
counter++;
}
}
C#:
public void SendTextByXpath(string xPath, string text)
{
Random random = new Random();
SwipeSearchElementByXpath(xPath);//Ищем элемент
Thread.Sleep(1000);
ClickToElementByXpath(xPath,2);//Кликаем по нему
instance.DroidInstance.Input.SendText(text, random.Next(100, 250)); // Вводим текст
instance.DroidInstance.Input.SendKeyCode(KeyCode.KEYCODE_ENTER); //Нажимаем enter после ввода текста
}
Класс и методы для запуска машины через лок и семафор
C#:
public class StartMachine
{
public static Semaphore SinhronizationStartingMachineSemaphore = new Semaphore(2, 2);//Статичный объект семафора 2,2 это то , сколько машин могут одновременно запускаться
public static object SinhronizationStartingMachineObj = new object();//Статичный объект лока
Instance instance;
IZennoPosterProjectModel project;
public StartMachine(Instance instance, IZennoPosterProjectModel project)
{
this.instance = instance;
this.project = project;
}
public void StartLock()
{
lock (SinhronizationStartingMachineObj)
{
instance.DroidInstance.Action.Stop();
instance.DroidInstance.Action.Start(true); // true значит что запускаем машину с применением настроек профиля, если не хотите их применять пропишите туда false
}
}
public void StartSemaphore()
{
SinhronizationStartingMachineSemaphore.WaitOne(); // Запрашиваем разрешение на доступ.
try
{
instance.DroidInstance.Action.Stop();
instance.DroidInstance.Action.Start(true);
}
finally
{
SinhronizationStartingMachineSemaphore.Release(); // Освобождаем разрешение после выполнения работы.
}
}
}
Все эти методы добавлены в общий код шаблона который я прикрепил к статье.
ZennoDroid + VisualStudio
Ну это прям для тертых калачей)
Прямого подруба vs к zd как vs к zp нету, придется мальца повозиться.
Сначала нам надо создать проект либы в студии.
Затем в папке проекта вам надо найти файл ProjectName.csproj (лежит рядом с файлом sln)
XML:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net4.8</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<Reference Include="ZennoLab.CommandCenter">
<HintPath>$(ZennoDroidDllPath)\ZennoLab.CommandCenter.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ZennoLab.Emulation">
<HintPath>$(ZennoDroidDllPath)\ZennoLab.Emulation.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ZennoLab.InterfacesLibrary">
<HintPath>$(ZennoDroidDllPath)\ZennoLab.InterfacesLibrary.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="ZennoDroid.Interface">
<HintPath>$(ZennoDroidDllPath)\ZennoDroid.Interface.dll</HintPath>
</Reference>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<StartAction>Program</StartAction>
<StartProgram>$(ZennoDroidDllPath)\ZennoLab.CodeRunner.exe</StartProgram>
<StartArguments>50606 "$(MSBuildThisFileDirectory)bin\Debug\net4.8\$(MSBuildProjectName).dll" -sp "$(USER_HOME)\AppData\Roaming\ZennoLab\ZennoPoster\7" --run-external-dll</StartArguments>
</PropertyGroup>
</Project>
Создать класс Program, унаследовать им интерфейс IZennoExternalCode и реализовать его.
Метод Execute является у нас точкой входа. Тут мы уже можем развернуться как следует, подключить другие проекты, насоздавать пространств имен, классов, методов и прочей бесовщины). Крупный проект на пример может вот так выглядеть.
А теперь расскажу как это в проект подкинуть. Кубика VisualStudio как в зенопостере у нас нету. Но я как то копался в документации Zennodroid и нашел там один пример проекта в котором этот кубик откуда то был, в итоге я его просто методом копирования переношу в свои проекты, с вами я им тоже поделюсь в файлах к статье.
В кубике мы уже просто указываем путь к dll нашей либы и запускаем его.
Практика
Ну а теперь давайте используя все полученные знания и напишем простенький шаблон для того что бы заработать нашу первую копеечку.
Для этого мы зарегаем кошелек монетки Neurai (XNA) и нападем с ним на кран.
Шаблон будет прикреплен к статье, код и кубики будут максимально прокомментированы.
Папка с шабом оказалась слишком большой, так что пришлось заливать в облако https://disk.yandex.ru/d/SkzBRgTfh9W9Ew
БОНУС
Решение Yandex Smart Captcha через клики.
Решение Yandex Smart Captcha через клики.
- Тема статьи
- Другое
- Номер конкурса статей
- Двадцатый конкурс статей
Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.
Последнее редактирование модератором: