Управление шаблоном c#

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Часто задают вопросы, как запустить шаба из шаба, как управлять, добавить потоки итд.
Люди начинают создавать некошерные батники и прочими образами извращаться, хотя зенка предоставляет не очень приличный, но достаточный api для решения этих вопросов более адекватным путем.
Выкладываю небольшой статический класс упрощающий работу с запуском вторичных шабов.

Класс вставляем в общий код после
Код:
namespace ZennoLab.OwnCode
{

Код:
public static class TaskHelper
   {
     
       public static string GetTaskId(string TaskName)
     {
       var tasksList = ZennoPoster.TasksList;
       foreach (var task in tasksList){         
        string tname = Regex.Match(task,@"(?<=<Name>).*?(?=</Name>)").Value;
          string tid  = Regex.Match(task,@"(?<=<Id>).*?(?=</Id><Name>)").Value;
         
          if (tname == TaskName){
           return tid;
          }
       }   
       return "";
     }
     
     public static int GetNumberOfTries(string TaskName)
     {
         string sid = TaskHelper.GetTaskId(TaskName);
       var id = Guid.Parse(sid);
       var taskInfo = ZennoPoster.GetTaskInfo(id);
       string execsettings = Regex.Match(taskInfo,@"(?<=<ExecutionSettings>).*?(?=</ExecutionSettings>)").Value;
       string ntries =  Regex.Match(execsettings,@"(?<=<NumberOfTries>).*?(?=</NumberOfTries>)").Value;
       int res=0;
       Int32.TryParse(ntries,out res);
       return res;       
     }
     
     public static string GetStatus(string TaskName)
     {
       string sid = TaskHelper.GetTaskId(TaskName);
       var id = Guid.Parse(sid);
       var taskInfo = ZennoPoster.GetTaskInfo(id);
       string status = Regex.Match(taskInfo,@"(?<=<Status>).*?(?=</Status>)").Value;
       return status;
       
       //Perform - работает
       //Complite - завершен
       //Stop - остановлен
       //Schedule - запланирован
       //WaitPerform - компилится
     }
     
     public static void WaitRun(IZennoPosterProjectModel project,string TaskName)
     {
         int nt = GetNumberOfTries(TaskName);
       string status = GetStatus(TaskName);
       while( nt >0 )
       {    
         if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) return; // Прерывание в ZP
         if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode) return; // Прерывание в PM
         
           Thread.Sleep(3000);
         
           nt = TaskHelper.GetNumberOfTries(TaskName);
         status = GetStatus(TaskName);
         
         switch (status)
         {
           case "Perform":
             break;
           case "Complite": nt = 0;
             break;
           case "Stop": nt = 0;
             break;
           case "Schedule":
             break;
           case "WaitPerform":
             break;
         }
         
           project.SendInfoToLog("Ожидаем завершения "+TaskName,true);   
       }
     }
   }
Примеры использования

Поиск айди шаблона по имени:
Код:
string tempname = "имя шаба";
if (TaskHelper.GetTaskId(tempname)==""){
  project.SendErrorToLog("Не смогли найти проект "+tempname, true);
  throw new Exception("Не найден проект");
}
Добавление нужного числа исполнений и ожидание завершения:
Код:
string tempname = "имя шаба";
int tries = 50; // будет добавлено 50 исполнений
if (tries>0){
   ZennoPoster.AddTries(tempname , tries);
   TaskHelper.WaitRun(project,tempname );  //ожидаем завершения всех исполнений
}
GetStatus - получить статус шаблона
Код:
//Perform - работает
//Complite - завершен
//Stop - остановлен
//Schedule - запланирован
//WaitPerform - компилится
GetNumberOfTries - получить кол-во заданых исполнений
 

Для запуска проектов требуется программа ZennoPoster.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

Для того чтобы запустить шаблон, откройте программу ZennoPoster. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Очень полезно и красиво! :-)

Расскажи только, пожалуйста, как работать с:
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) return; // Прерывание в ZP
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode) return; // Прерывание в PM
?
 
  • Спасибо
Реакции: viol2021

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Есть снипет где вызван WaitRun. Соответственно, пока вторичный шаб не завершит работу мы в этом снипе и крутимся в цикле.
Соответственно для выхода из цикла при остановке по кнопке стоп из PM или "прервать" в зенке надо ловить соответствующие события, что эти две строки и делают - проверяют был ли шаб остановлен стопом (pm) или прерыванием (ZP) и выходит из цикла простым ретурном и шаб завершается.
 
  • Спасибо
Реакции: Lord_Alfred

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Соответственно для выхода из цикла при остановке по кнопке стоп из PM или "прервать" в зенке надо ловить соответствующие события
Это именно отлов событий, а не необходимость создавать глобальные переменные / контекст, чтобы остановить шаблон?
Просто не встречал нигде описания такого способа отлова этих событий, вот и удивился :-) Незадокументированные фичи?)
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Это именно отлов событий, а не необходимость создавать глобальные переменные / контекст, чтобы остановить шаблон?
Ненене, ничего создавать не надо.. это "родные" зенновские глобалки.

Просто не встречал нигде описания такого способа отлова этих событий, вот и удивился :-) Незадокументированные фичи?)
Я их и сам тут на форуме нашел) Хз, может и недокументированные.
 
  • Спасибо
Реакции: Lord_Alfred

systema

Client
Регистрация
25.07.2013
Сообщения
174
Благодарностей
64
Баллы
28
Все разобрался.
Спасибо за код, то что нужно :ay:
 
Последнее редактирование:

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 352
Благодарностей
3 279
Баллы
113

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Быть может у кого-то есть идеи или наработки как запускать шаблон, но при этом передавая туда ещё и конфиг какой-то? (без использования файлов и прочего - чисто конфиг, который мы в зенке выставляем у шаблона)

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

Звучит дико извращенно, но от того и интересно это)
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Просто тут мысль в голову пришла, что было бы интересно допилить этот класс до реализации подобия запуска асинхронных тасков с передачей аргументов каких-то в шаблон.
Перед запуском субпроекта ему входные настройки установить через SetExecutionSettings.
Но хз как это будет отрабатывать в случае "установил, запустил, не дождался завершения снова установил, еще одно исполнение добавил".

В принципе, для подобной задачи я этот класс и реализовывал, только задача была не асинхронка, а быстро отработать пул задач по парсингу, используя многопоток и дополнительные аккаунты. Т.е. изначально первичный проект делал всю задачу от и до. Но проект выполнялся двое-трое суток.
А так первичный проект выпарсивает первичные данные и пишет их в табличку, где каждая запись - отдельная задача для субпроекта. Дальше запускается субпроект многопотоком, для каждого потока подсасывается свободный аккаунт, свободный прокси и тд подтягивается через лок ОДНА задача из таблички и производится обработка. Результат пишется в ту же табличку в отдельное поле. Для другого проекта - в отдельную табличку с указанием айдишника задачи. Максимальное количество потоков для субпроекта стоит либо зависимо от мощности компа, либо зависимо от количества доступных "рабочих" аккаунтов. Субпроект при завершении ставит аккаунту и прокси статус "свободен" и завершает работу.
Первичный проект ждет завершения обработки и продолжает свое дело уже на основе всех обработанных данных.
В итоге 2-5 часов на обработку.

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

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Но хз как это будет отрабатывать в случае "установил, запустил, не дождался завершения снова установил, еще одно исполнение добавил".
Да, тоже вот уже после того как запостил - задумался о том, что будет оверхед на валидацию того дошли ли настройки до запуска и не нужно ли ожидать ещё. Ещё потом была мысль, что возможно поможет какой-нибудь Parallel.For и внутри просто запуск подпроекта на C# (т.к. там даже в PM табы не открываются в случае запуска через C# метод). Скорее всего такой дикий вариант буду пробовать или вообще по-другому архитектуру построю - пока не знаю)
 

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Обновлю информацию по задуманной в предыдущем посте дичи. Пишу отдельным, чтоб увидел @DmitryAk и с радостью бы послушал @amyboose или @Adigen.

Вообщем, получилось запустить асинхронно вложенный шаблон с помощью такой вот конструкции:

Общий код:
C#:
using System.Threading.Tasks;

namespace ZennoLab.OwnCode
{
    public class Runner {
        public static void Inner(IZennoPosterProjectModel project) {
            var mapVars = new List<Tuple<string, string>>();
            mapVars.Add(new Tuple<string, string>("var1", "IN_var1")); // маппинг переменных из родительского шаблона => в дочерний
            string project_path = Path.Combine(project.Directory, "template_name.xmlz"); // имя дочернего шаблона
            project.ExecuteProject(project_path, mapVars, true, false, false);
        }
      
        public static void Run(IZennoPosterProjectModel project) {
            System.Threading.Tasks.Task.Run(() => Inner(project));
        }
    }
}
C# сниппет:
C#:
Runner.Run(project);
Да, запускается он асинхронно и вроде как отрабатывает до конца, при этом не блокируя родительский процесс. Но меня, конечно, очень смущает эта идея уже :-) Сделал её уже просто из спортивного интереса, но, к моему сожалению, совсем не понимаю как оно будет распределяться по ядрам в случае ударной нагрузки? И есть ли какие-то таймауты, по которым может таск отвалится и недоработать? А память не потечет от такой дичи?
Уже не говорю про то, что нужно точно знать завершился таск до конца или нет, т.к. по счастливой случайности в конце я пишу файл, который можно потом будет почекать.
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Хорошее решение.. т.е. это запуск "проекта в проекте" отдельным потоком по факту. "Дичью" это назвать нельзя, по идее это вполне нормальный подход. Течь не должно.. наверное, но это не точно))
Имхо юзать не слишком удобно.. замэпить все переменные надо.

А на браузерном проекте тестил? Т.е. и первичный и вторичный браузерные? Как себя ведет?
 
  • Спасибо
Реакции: Lord_Alfred

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Имхо юзать не слишком удобно.. замэпить все переменные надо.
Да, меня эта реализация передачи переменных тоже немного напрягает(

А на браузерном проекте тестил? Т.е. и первичный и вторичный браузерные? Как себя ведет?
Неа, к сожалению, не потестил. У меня задача на безбраузерных шабах сейчас построена, допилил вот и выложил сразу как понял что работает) Даже толком не погонял ведь, чтоб наверняка говорить.
Мне вообще кажется, что такими "ухищрениями" по отношению к версиям zp ниже максималки (пишу чуть завуалировано, сорян) занимался Амибозыч, я помню такое очень давно он где-то рассказывал ВладЗену это, там как раз вроде он и тестил похожие подходы. Не скажу только решение 1 в 1 было или чем-то отличалось
 

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Вообщем, поюзал я решение запуска асинхронных подпроектов, что выложил парой постов выше. Стоит рассказать о паре особенностей, которые заметил чтоб другие знали.
  1. Если писать в лог в этом асинхронном проекте, то месседж дойдёт до туда (что очевидно, но удивительно для меня). Причем дойдет он до туда, даже если родительский проект уже завершился. В PM так точно такая схема работает, что удобно. UPD: пишет в лог, но не всегда. Иногда пишет всё подряд, даже каждое выполненное действие, а иногда не пишет это. Вообщем, нужно быть осторожнее.
  2. Словил странный баг: в лог приходило сообщение с ошибкой "Последовательность не содержит что-то там", по но ID кубик не находился и при запуске "вручную" - такой ошибки не было. Отсюда, думаю, стоит заключить, что скорее всего из-за этого хитрого запуска - шаб не перекомпилируется, хотя должен. После перезапуска PM ошибка пропала, но вначале я потратил достаточно времени, чтоб понять почему она валится, почему не ищется по ID и где же всё таки она может быть.
 
Последнее редактирование:
  • Спасибо
Реакции: iwbh и Yuriy Zymlex

ngusev

Client
Регистрация
26.06.2013
Сообщения
9
Благодарностей
3
Баллы
3
Доброго дня, не хватает стандартного планировщика. Тоже возник вопрос по позданию шаблона для расширенного управления выполнения шаблонов по расписанию
на данный момент необходимо:
1)Выполнение определенного количества рандомно выполненных заданий в заданый промежуток времени
Пример:Мне нужно лайкнуть свой пост 12 раз разными профилями с 9 00 утра до 11 00 , соответственно шаблон нужно
запустить 12 раз рандомно в этот промежуток времени(вариант лайкать каждые 10 минут меня не устраивает)
2)Выставлять разные промежутки времени и количество выполнений для дней недели и определенных дат
таких как праздничные дни.
3)Создавать разные сценарии выполнения под каждый шаблон.

Кто уже делал , интересует консультация или покупка , может у вас функционал больше , готов обсуждать !
 

molotok

Client
Регистрация
17.04.2015
Сообщения
733
Благодарностей
358
Баллы
63
А куда возвращает id шаблона этот код?
Код:
string tempname = "имя шаба";
if (TaskHelper.GetTaskId(tempname)==""){
  project.SendErrorToLog("Не смогли найти проект "+tempname, true);
  throw new Exception("Не найден проект");
}
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
А куда возвращает id шаблона этот код
В данном случае в условие для проверки. можно дернуть айдишник с возвратом снипетом типа
Код:
string tempname = "имя шаба"; return TaskHelper.GetTaskId(tempname);
 
  • Спасибо
Реакции: Mikhail B. и molotok

molotok

Client
Регистрация
17.04.2015
Сообщения
733
Благодарностей
358
Баллы
63
В данном случае в условие для проверки. можно дернуть айдишник с возвратом снипетом типа
Код:
string tempname = "имя шаба"; return TaskHelper.GetTaskId(tempname);
Я примерно так и сделал, но новички, которые будут просто копировать код, могут не понять почему не работает...

Код:
string tempname = "claimBot2_Get_0.1.2_2";
string strId = TaskHelper.GetTaskId(tempname);
if (strId==String.Empty){
  project.SendErrorToLog("Не смогли найти проект "+tempname, true); throw new Exception("Не найден проект");
  }else{
    project.SendErrorToLog(strId, true);
  }
Кстати, в общем коде, в методе GetTaskId можно добавить перевод сравниваемых строк в нижний регистр. Так как почему-то название шаблона в Зенке начинается с заглавной буквы, но при этом сам файл шаблона начинается с прописной.
 
  • Спасибо
Реакции: JurgenZolle

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 352
Благодарностей
3 279
Баллы
113
Имхо юзать не слишком удобно.. замэпить все переменные надо.
Если шабы свои, то проще передать объект через project.Context в удобном виде. Не забыв passProjectContext в true.
название шаблона в Зенке начинается с заглавной буквы, но при этом сам файл шаблона начинается с прописной
В конфиге имя файла присутствует, можно и с ним сравнивать.

Вообще, через XmlReader поудобней будет, чем регулярками.:-)
ConformanceLevel.Fragment позволяет с таким форматом работать.
 
Последнее редактирование:

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Вообщем, получилось запустить асинхронно вложенный шаблон с помощью такой вот конструкции:
Апдейтну ещё раз сам себя. Есть подозрения, что именно такой запуск подпроекта вместе с использованием куки-контейнера приводит к "смешиванию" кук. То есть в текущем аккаунте могут оказаться куки от предыдущего аккаунта. Ловил такое поведение в PM, как будет в ZP - без понятия.
Очень прошу модераторм апдейтнуть тот пост этой инфой и спрятать код под спойлер, чтоб кто-то не нахватал таких же багов. Сам не могу редактировать уже :(

Я у себя буду избавляться от такого запуска, буду переводить на использование файла/БД, куда буду помещать данные для запуска следующего потока для парсинга. А уже самой зенкой буду запускать каждую минуту этот таск, чтоб проверить не появилось ли чего нового.
Да, использовать выложенный мной выше вариант гораздо интереснее, но пойманный баг с куки-контейнером меня очень сильно огорчает :(
 
  • Спасибо
Реакции: Yuriy Zymlex

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 352
Благодарностей
3 279
Баллы
113
Есть подозрения, что именно такой запуск подпроекта вместе с использованием куки-контейнера приводит к "смешиванию" кук.
Для запросов, можно и отдельный куки-контейнер создать, но если требуется работа с инстансом, то только пробовать поднимать под каждый подпроект свой.
Лучше запускать через AddTask с inputsettings, но с получением результата будет проблемно.
 
Последнее редактирование:

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
Для запросов, можно и отдельный куки-контейнер создать, но если требуется работа с инстансом, то только пробовать поднимать под каждый подпроект свой.
Я там кубиками запрос делал, жаль ты раньше не написал - переделал бы на C# кубики. Сам как-то не догадался)
Теперь в mysql базу пишу параметры запуска (входные настройки грубо говоря), а уже самой зенкой каждую минуту дергаю не появилось ли новых задач. Такая себе асинхронность, но зато без багов :-)
 

Yuriy Zymlex

Moderator
Команда форума
Регистрация
24.10.2016
Сообщения
6 352
Благодарностей
3 279
Баллы
113
Быть может у кого-то есть идеи или наработки как запускать шаблон, но при этом передавая туда ещё и конфиг какой-то?
Как пример, выложил свой старый код по запуску,
не в совсем лучшем виде, но сами допишите. :-)
Надо только ImportInputSettings добавить и будут как настройки.
 

Вложения

  • Спасибо
Реакции: iwbh и Lord_Alfred

bizzon

Client
Регистрация
08.09.2015
Сообщения
1 084
Благодарностей
126
Баллы
63
Совсем нуб, не знаком с понятиями Общий код и т.д.
Выдает "Имя "TaskHelper" отсутствует в текущем контексте"
Что сделал не так?
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Да, очевидно, не вставил код класса TaskHelper в общий код.
 

bizzon

Client
Регистрация
08.09.2015
Сообщения
1 084
Благодарностей
126
Баллы
63

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
  • Спасибо
Реакции: bizzon

inbox

Client
Регистрация
11.11.2018
Сообщения
11
Благодарностей
3
Баллы
3
Часто задают вопросы, как запустить шаба из шаба, как управлять, добавить потоки итд.
Люди начинают создавать некошерные батники и прочими образами извращаться, хотя зенка предоставляет не очень приличный, но достаточный api для решения этих вопросов более адекватным путем.
Выкладываю небольшой статический класс упрощающий работу с запуском вторичных шабов.
Спасибо друг, очень пригодился твой код. Систематизировал знания мои.
 

Lord_Alfred

Client
Регистрация
09.10.2015
Сообщения
3 916
Благодарностей
3 852
Баллы
113
PS: @DmitryAk спасибо ещё раз за:
C#:
if(((ZennoLab.InterfacesLibrary.ProjectModel.Collections.IContextExt)project.Context).IsInterrupted) return; // Прерывание в ZP
if(Global.Variables.IsProjectMaker && !Global.Variables.IsDebugMode) return; // Прерывание в PM
Очень пригодилось) Правда, пришлось на throw new Exception заменить, а то чет компилятор на return ругался )
 

DmitryAk

Client
Регистрация
14.12.2016
Сообщения
860
Благодарностей
817
Баллы
93
Да пожалуйста)
Очень пригодилось) Правда, пришлось на throw new Exception заменить, а то чет компилятор на return ругался )
Ну ретурн, это для сниппетов - выход по красной олд стайле или войд функций - мягкое прерывание без ошибок)
 
  • Спасибо
Реакции: Lord_Alfred

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