#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from functions import *
from bs4 import BeautifulSoup
import http.cookiejar as cookielib
import codecs
import os
import requests
def filterProblems( problems1, problems2) :
for key in problems2:
if key in problems1:
del problems1[ key]
return problems1
class Extractor( object ) :
def __init__ ( self ) :
self .s = requests.session ( )
self .lang = ""
self .folders = [ ]
self .header = [ ]
self .tokenFields = "41dcc576d590c4d85784392529146d228d160ebf%3A"
def extractProblems( self , soup, problems) :
itens = soup.find_all ( 'table' ) [ 0 ] .find_all ( 'tbody' ) [ 0 ] .find_all ( 'tr' )
for tr in itens:
#print(tr)
td = tr.find_all ( 'td' )
if len ( td) != 8 :
return
else :
problems[ td[ 0 ] .find ( 'a' ) .text .strip ( ) ] = {
"id_code" : td[ 2 ] .find ( 'a' ) .text .strip ( ) ,
"name" : td[ 3 ] .find ( 'a' ) .text .strip ( ) ,
"language" : td[ 4 ] .text .strip ( )
}
def folderStructure( self ) :
if not os .path .exists ( './URI-' +self .lang .upper ( ) +'/' ) :
os .makedirs ( './URI-' +self .lang .upper ( ) +'/' )
for name in self .folders :
if not os .path .exists ( './URI-' +self .lang .upper ( ) +'/' +name) :
os .makedirs ( './URI-' +self .lang .upper ( ) +'/' +name)
def login( self ) :
username = input ( '\n \n Login e-mail: ' )
password = input ( 'Password: ' )
self .tokenFields = self .getPage ( "https://w...content-available-to-author-only...m.br/judge/pt/login" ) .find ( "input" , { "name" : "_Token[fields]" } ) .attrs [ 'value' ]
payload_login = {
'_method' : 'POST' ,
'_csrfToken' : self .s .cookies [ 'csrfToken' ] ,
'email' : username,
'password' : password,
'remember_me' : '0' ,
'_Token[fields]' : self .tokenFields ,
'_Token[unlocked]' : ''
}
r = self .s .post ( "https://w...content-available-to-author-only...m.br/judge/pt/login" , data= payload_login)
if len ( r.history ) == 0 :
print ( "Invalid login. Try Again" )
return False
self .lang = r.url .split ( '/' ) [ -1 ]
self .email = username
self .author = self .getPage ( 'https://w...content-available-to-author-only...m.br/judge/' +self .lang +'/account' ) .find ( "input" , { "name" : "username" } ) .attrs [ 'value' ]
if self .lang == 'pt' :
self .folders = [ "INICIANTE" , "AD-HOC" , "STRINGS" , "ESTRUTURAS E BIBLIOTECAS" , "MATEMÁTICA" , "PARADIGMAS" , "GRAFOS" , "GEOMETRIA COMPUTACIONAL" , "SQL" ]
self .header = [ " Autor: " +self .author + "<" +self .email +">" , " Nome: " , " Nível: " , " Categoria: " ]
return [ "Login inválido" , "\n Logado com sucesso\n " , "Estrutura de pastas criada" , "Criando lista de download" , "Lista de download criada: " , " problemas" , "\n Baixando códigos" , "\n Códigos baixados com sucesso" , "Nome completo: " , "Email de contato: " ]
elif self .lang == 'en' :
self .folders = [ "BEGINNER" , "AD-HOC" , "STRINGS" , "DATA STRUCTURES AND LIBRARIES" , "MATHEMATICS" , "PARADIGMS" , "GRAPH" , "COMPUTATIONAL GEOMETRY" , "SQL" ]
self .header = [ " Author: " +self .author + "<" +self .email +">" , " Name: " , " Level: " , " Category: " ]
return [ "Invalid login" , "\n Successfully logged in\n " , "Folder structure created" , "Creating download list" , "Download list created: " , " problems" , "\n Downloading Codes" , "\n Codes successfully downloaded" , "Full name: " , "Email contact: " ]
elif self .lang == 'es' :
self .folders = [ "INICIANTE" , "AD-HOC" , "STRINGS" , "ESTRUTURAS E BIBLIOTECAS" , "MATEMÁTICA" , "PARADIGMAS" , "GRAFOS" , "GEOMETRIA COMPUTACIONAL" , "SQL" ]
self .header = [ " Autor: " +self .author + "<" +self .email +">" , " Nome: " , " Nível: " , " Categoria: " ]
return [ "Login inválido" , "\n Logado com sucesso\n " , "Estrutura de pastas criada" , "Criando lista de download" , "Lista de download criada: " , " problemas" , "\n Baixando códigos" , "\n Códigos baixados com sucesso" , "Nome completo: " , "Email de contato: " ]
def getPage( self , url) :
return BeautifulSoup( self .s .get ( url) .content , 'html.parser' )
def getUriProblems( self ) :
qt = int ( self .getPage ( 'https://w...content-available-to-author-only...m.br/judge/' +self .lang +'/runs?answer_id=1' ) .find ( "div" , { "id" : "table-info" } ) .text .split ( " " ) [ 6 ] )
#print(qt)
problems = { }
for i in range ( qt, 0 , -1 ) :
self .extractProblems ( self .getPage ( 'https://w...content-available-to-author-only...m.br/judge/' +self .lang +'/runs?answer_id=1&page=' +str ( i) ) , problems)
#print(len(problems))
return problems
def getLocalProblems( self ) :
problems = [ ]
for path in os .listdir ( './URI-' +self .lang .upper ( ) +'/' ) :
for arq in os .listdir ( './URI-' +self .lang .upper ( ) +'/' +path+'/' ) :
problems.append ( arq.rsplit ( '.' , 1 ) [ 0 ] .split ( '-' ) [ 0 ] .strip ( ) )
#print(problems)
return problems
def infoProblem( self , id ) :
menu = self .getPage ( 'https://w...content-available-to-author-only...m.br/judge/' +self .lang +'/problems/view/' +id ) .find ( "div" , { "id" : "problem-menu" } )
#print("KKKK----", menu)
return {
"level" : "1" ,
"category" : "Beginner"
}
def getCode( self , id ) :
print ( ( self .getPage ( 'https://w...content-available-to-author-only...m.br/judge/' +self .lang +'/runs/code/' +id ) .find ( "pre" , { "id" : "code" } ) ) )
def cleanName( self , name) :
return name.replace ( "\\ " , "" ) .replace ( "/" , "" ) .replace ( ":" , "" ) .replace ( "?" , "" ) .replace ( "\" " , "" ) .replace ( "<" , "" ) .replace ( ">" , "" ) .replace ( "|" , "" )
def downloadCodes( self , problems) :
for id in problems:
if self .lang == "pt" :
print ( "\t Problema " +id +" baixado" )
else :
print ( "\t Problem " +id +" downloaded" )
info = self .infoProblem ( id )
code = self .getCode ( problems[ id ] [ "id_code" ] )
extension = ""
comment = "//"
if problems[ id ] [ "language" ] == "C++" :
extension = ".cpp"
elif problems[ id ] [ "language" ] == "C" :
extension = ".c"
elif problems[ id ] [ "language" ] == "Java" :
extension = ".java"
else :
extension = ".py"
comment = "#"
arq = codecs .open ( "./URI-" +self .lang .upper ( ) +"/" +info[ "category" ] +"/" +id +" - " +self .cleanName ( problems[ id ] [ "name" ] ) +extension, "w" , "utf-8" )
arq.write ( comment+self .header [ 0 ] +"\n " )
arq.write ( comment+self .header [ 1 ] +problems[ id ] [ "name" ] +"\n " )
arq.write ( comment+self .header [ 2 ] +info[ "level" ] +"\n " )
arq.write ( comment+self .header [ 3 ] +info[ "category" ] +"\n " )
arq.write ( comment+" URL: https://w...content-available-to-author-only...m.br/judge/" +self .lang +"/problems/view/" +id +"\n \n " )
arq.write ( code )
arq.write ( "\n " )
arq.close ( )
extractor = Extractor( )
message = extractor.login ( )
while message == False :
message = extractor.login ( )
print ( message[ 1 ] )
extractor.folderStructure ( )
print ( message[ 2 ] )
print ( message[ 3 ] )
download_list = filterProblems( extractor.getUriProblems ( ) , extractor.getLocalProblems ( ) )
print ( message[ 4 ] +str ( len ( download_list) ) +message[ 5 ] )
print ( message[ 6 ] )
extractor.downloadCodes ( download_list)
print ( message[ 7 ] )
IyEvdXNyL2Jpbi9lbnYgcHl0aG9uCiMgLSotIGNvZGluZzogdXRmLTggLSotCmZyb20gX19mdXR1cmVfXyBpbXBvcnQgcHJpbnRfZnVuY3Rpb24KZnJvbSBmdW5jdGlvbnMgaW1wb3J0ICoKZnJvbSBiczQgaW1wb3J0IEJlYXV0aWZ1bFNvdXAKaW1wb3J0IGh0dHAuY29va2llamFyIGFzIGNvb2tpZWxpYgppbXBvcnQgY29kZWNzCmltcG9ydCBvcwppbXBvcnQgcmVxdWVzdHMKCmRlZiBmaWx0ZXJQcm9ibGVtcyhwcm9ibGVtczEsIHByb2JsZW1zMik6CiAgICBmb3Iga2V5IGluIHByb2JsZW1zMjoKICAgICAgICBpZiBrZXkgaW4gcHJvYmxlbXMxOgogICAgICAgICAgICBkZWwgcHJvYmxlbXMxW2tleV0KICAgIHJldHVybiBwcm9ibGVtczEKCmNsYXNzIEV4dHJhY3RvcihvYmplY3QpOgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgIHNlbGYucyA9IHJlcXVlc3RzLnNlc3Npb24oKQogICAgICAgIHNlbGYubGFuZyA9ICIiCiAgICAgICAgc2VsZi5mb2xkZXJzID0gW10KICAgICAgICBzZWxmLmhlYWRlciA9IFtdCiAgICAgICAgc2VsZi50b2tlbkZpZWxkcyA9ICI0MWRjYzU3NmQ1OTBjNGQ4NTc4NDM5MjUyOTE0NmQyMjhkMTYwZWJmJTNBIgogICAgICAgICAgICAKICAgIGRlZiBleHRyYWN0UHJvYmxlbXMoc2VsZiwgc291cCwgcHJvYmxlbXMpOgogICAgICAgIGl0ZW5zID0gc291cC5maW5kX2FsbCgndGFibGUnKVswXS5maW5kX2FsbCgndGJvZHknKVswXS5maW5kX2FsbCgndHInKQogICAgICAgIGZvciB0ciBpbiBpdGVuczoKICAgICAgICAgICAgI3ByaW50KHRyKQogICAgICAgICAgICB0ZCA9IHRyLmZpbmRfYWxsKCd0ZCcpCiAgICAgICAgICAgIGlmIGxlbih0ZCkgIT0gODoKICAgICAgICAgICAgICAgIHJldHVybiAKICAgICAgICAgICAgZWxzZTogICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICBwcm9ibGVtc1t0ZFswXS5maW5kKCdhJykudGV4dC5zdHJpcCgpXSA9IHsKICAgICAgICAgICAgICAgICAgICAiaWRfY29kZSI6IHRkWzJdLmZpbmQoJ2EnKS50ZXh0LnN0cmlwKCksCiAgICAgICAgICAgICAgICAgICAgIm5hbWUiOiB0ZFszXS5maW5kKCdhJykudGV4dC5zdHJpcCgpLAogICAgICAgICAgICAgICAgICAgICJsYW5ndWFnZSI6IHRkWzRdLnRleHQuc3RyaXAoKQogICAgICAgICAgICAgICAgfQoKICAgIGRlZiBmb2xkZXJTdHJ1Y3R1cmUoc2VsZik6CiAgICAgICAgaWYgbm90IG9zLnBhdGguZXhpc3RzKCcuL1VSSS0nK3NlbGYubGFuZy51cHBlcigpKycvJyk6CiAgICAgICAgICAgIG9zLm1ha2VkaXJzKCcuL1VSSS0nK3NlbGYubGFuZy51cHBlcigpKycvJykKICAgICAgICBmb3IgbmFtZSBpbiBzZWxmLmZvbGRlcnM6CiAgICAgICAgICAgIGlmIG5vdCBvcy5wYXRoLmV4aXN0cygnLi9VUkktJytzZWxmLmxhbmcudXBwZXIoKSsnLycrbmFtZSk6CiAgICAgICAgICAgICAgICBvcy5tYWtlZGlycygnLi9VUkktJytzZWxmLmxhbmcudXBwZXIoKSsnLycrbmFtZSkKCiAgICBkZWYgbG9naW4oc2VsZik6CiAgICAgICAgdXNlcm5hbWUgPSBpbnB1dCgnXG5cbkxvZ2luIGUtbWFpbDogJykKICAgICAgICBwYXNzd29yZCA9IGlucHV0KCdQYXNzd29yZDogJykKICAgICAgICBzZWxmLnRva2VuRmllbGRzID0gc2VsZi5nZXRQYWdlKCJodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0uYnIvanVkZ2UvcHQvbG9naW4iKS5maW5kKCJpbnB1dCIsIHsibmFtZSI6ICJfVG9rZW5bZmllbGRzXSJ9KS5hdHRyc1sndmFsdWUnXQogICAgICAgIHBheWxvYWRfbG9naW4gPSB7CiAgICAgICAgICAgICdfbWV0aG9kJzogJ1BPU1QnLAogICAgICAgICAgICAnX2NzcmZUb2tlbic6IHNlbGYucy5jb29raWVzWydjc3JmVG9rZW4nXSwKICAgICAgICAgICAgJ2VtYWlsJzogdXNlcm5hbWUsCiAgICAgICAgICAgICdwYXNzd29yZCc6IHBhc3N3b3JkLAogICAgICAgICAgICAncmVtZW1iZXJfbWUnOiAnMCcsCiAgICAgICAgICAgICdfVG9rZW5bZmllbGRzXSc6IHNlbGYudG9rZW5GaWVsZHMsCiAgICAgICAgICAgICdfVG9rZW5bdW5sb2NrZWRdJzogJycKICAgICAgICB9CgogICAgICAgIHIgPSBzZWxmLnMucG9zdCgiaHR0cHM6Ly93Li4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5tLmJyL2p1ZGdlL3B0L2xvZ2luIiwgZGF0YT1wYXlsb2FkX2xvZ2luKQoKICAgICAgICBpZiBsZW4oci5oaXN0b3J5KSA9PSAwOgogICAgICAgICAgICBwcmludCgiSW52YWxpZCBsb2dpbi4gVHJ5IEFnYWluIikKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCgogICAgICAgIHNlbGYubGFuZyA9IHIudXJsLnNwbGl0KCcvJylbLTFdCgogICAgICAgIHNlbGYuZW1haWwgPSB1c2VybmFtZQogICAgICAgIHNlbGYuYXV0aG9yID0gc2VsZi5nZXRQYWdlKCdodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0uYnIvanVkZ2UvJytzZWxmLmxhbmcrJy9hY2NvdW50JykuZmluZCgiaW5wdXQiLCB7Im5hbWUiOiAidXNlcm5hbWUifSkuYXR0cnNbJ3ZhbHVlJ10KCiAgICAgICAgaWYgc2VsZi5sYW5nID09ICdwdCc6CiAgICAgICAgICAgIHNlbGYuZm9sZGVycyA9IFsiSU5JQ0lBTlRFIiwgIkFELUhPQyIsICJTVFJJTkdTIiwgIkVTVFJVVFVSQVMgRSBCSUJMSU9URUNBUyIsICJNQVRFTcOBVElDQSIsICJQQVJBRElHTUFTIiwgIkdSQUZPUyIsICJHRU9NRVRSSUEgQ09NUFVUQUNJT05BTCIsICJTUUwiXQogICAgICAgICAgICBzZWxmLmhlYWRlciA9IFsiIEF1dG9yOiAiK3NlbGYuYXV0aG9yKyAiPCIrc2VsZi5lbWFpbCsiPiIsICIgTm9tZTogIiwgIiBOw612ZWw6ICIsICIgQ2F0ZWdvcmlhOiAiXQogICAgICAgICAgICByZXR1cm4gWyJMb2dpbiBpbnbDoWxpZG8iLCAiXG5Mb2dhZG8gY29tIHN1Y2Vzc29cbiIsICJFc3RydXR1cmEgZGUgcGFzdGFzIGNyaWFkYSIsICJDcmlhbmRvIGxpc3RhIGRlIGRvd25sb2FkIiwgIkxpc3RhIGRlIGRvd25sb2FkIGNyaWFkYTogIiwgIiBwcm9ibGVtYXMiLCAiXG5CYWl4YW5kbyBjw7NkaWdvcyIsICJcbkPDs2RpZ29zIGJhaXhhZG9zIGNvbSBzdWNlc3NvIiwgIk5vbWUgY29tcGxldG86ICIsICJFbWFpbCBkZSBjb250YXRvOiAiXQogICAgICAgIGVsaWYgc2VsZi5sYW5nID09ICdlbic6CiAgICAgICAgICAgIHNlbGYuZm9sZGVycyA9IFsiQkVHSU5ORVIiLCAiQUQtSE9DIiwgIlNUUklOR1MiLCAiREFUQSBTVFJVQ1RVUkVTIEFORCBMSUJSQVJJRVMiLCAiTUFUSEVNQVRJQ1MiLCAiUEFSQURJR01TIiwgIkdSQVBIIiwgIkNPTVBVVEFUSU9OQUwgR0VPTUVUUlkiLCAiU1FMIl0KICAgICAgICAgICAgc2VsZi5oZWFkZXIgPSBbIiBBdXRob3I6ICIrc2VsZi5hdXRob3IrICI8IitzZWxmLmVtYWlsKyI+IiwgIiBOYW1lOiAiLCAiIExldmVsOiAiLCAiIENhdGVnb3J5OiAiXQogICAgICAgICAgICByZXR1cm4gWyJJbnZhbGlkIGxvZ2luIiwgIlxuU3VjY2Vzc2Z1bGx5IGxvZ2dlZCBpblxuIiwgIkZvbGRlciBzdHJ1Y3R1cmUgY3JlYXRlZCIsICJDcmVhdGluZyBkb3dubG9hZCBsaXN0IiwgIkRvd25sb2FkIGxpc3QgY3JlYXRlZDogIiwgIiBwcm9ibGVtcyIsICJcbkRvd25sb2FkaW5nIENvZGVzIiwgIlxuQ29kZXMgc3VjY2Vzc2Z1bGx5IGRvd25sb2FkZWQiLCAiRnVsbCBuYW1lOiAiLCAiRW1haWwgY29udGFjdDogIl0KICAgICAgICBlbGlmIHNlbGYubGFuZyA9PSAnZXMnOgogICAgICAgICAgICBzZWxmLmZvbGRlcnMgPSBbIklOSUNJQU5URSIsICJBRC1IT0MiLCAiU1RSSU5HUyIsICJFU1RSVVRVUkFTIEUgQklCTElPVEVDQVMiLCAiTUFURU3DgVRJQ0EiLCAiUEFSQURJR01BUyIsICJHUkFGT1MiLCAiR0VPTUVUUklBIENPTVBVVEFDSU9OQUwiLCAiU1FMIl0KICAgICAgICAgICAgc2VsZi5oZWFkZXIgPSBbIiBBdXRvcjogIitzZWxmLmF1dGhvcisgIjwiK3NlbGYuZW1haWwrIj4iLCAiIE5vbWU6ICIsICIgTsOtdmVsOiAiLCAiIENhdGVnb3JpYTogIl0KICAgICAgICAgICAgcmV0dXJuIFsiTG9naW4gaW52w6FsaWRvIiwgIlxuTG9nYWRvIGNvbSBzdWNlc3NvXG4iLCAiRXN0cnV0dXJhIGRlIHBhc3RhcyBjcmlhZGEiLCAiQ3JpYW5kbyBsaXN0YSBkZSBkb3dubG9hZCIsICJMaXN0YSBkZSBkb3dubG9hZCBjcmlhZGE6ICIsICIgcHJvYmxlbWFzIiwgIlxuQmFpeGFuZG8gY8OzZGlnb3MiLCAiXG5Dw7NkaWdvcyBiYWl4YWRvcyBjb20gc3VjZXNzbyIsICJOb21lIGNvbXBsZXRvOiAiLCAiRW1haWwgZGUgY29udGF0bzogIl0KCgogICAgZGVmIGdldFBhZ2Uoc2VsZiwgdXJsKToKICAgICAgICByZXR1cm4gQmVhdXRpZnVsU291cChzZWxmLnMuZ2V0KHVybCkuY29udGVudCwgJ2h0bWwucGFyc2VyJykKCiAgICBkZWYgZ2V0VXJpUHJvYmxlbXMoc2VsZik6CiAgICAgICAgcXQgPSBpbnQoc2VsZi5nZXRQYWdlKCdodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0uYnIvanVkZ2UvJytzZWxmLmxhbmcrJy9ydW5zP2Fuc3dlcl9pZD0xJykuZmluZCgiZGl2IiwgeyJpZCI6ICJ0YWJsZS1pbmZvIn0pLnRleHQuc3BsaXQoIiAiKVs2XSkKICAgICAgICAjcHJpbnQocXQpCiAgICAgICAgcHJvYmxlbXMgPSB7fQogICAgICAgIGZvciBpIGluIHJhbmdlKHF0LCAwLCAtMSk6CiAgICAgICAgICAgIHNlbGYuZXh0cmFjdFByb2JsZW1zKHNlbGYuZ2V0UGFnZSgnaHR0cHM6Ly93Li4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5tLmJyL2p1ZGdlLycrc2VsZi5sYW5nKycvcnVucz9hbnN3ZXJfaWQ9MSZwYWdlPScrc3RyKGkpKSwgcHJvYmxlbXMpCiAgICAgICAgICAgICNwcmludChsZW4ocHJvYmxlbXMpKQogICAgICAgIHJldHVybiBwcm9ibGVtcwoKICAgIGRlZiBnZXRMb2NhbFByb2JsZW1zKHNlbGYpOgogICAgICAgIHByb2JsZW1zID0gW10KICAgICAgICBmb3IgcGF0aCBpbiBvcy5saXN0ZGlyKCcuL1VSSS0nK3NlbGYubGFuZy51cHBlcigpKycvJyk6CiAgICAgICAgICAgIGZvciBhcnEgaW4gb3MubGlzdGRpcignLi9VUkktJytzZWxmLmxhbmcudXBwZXIoKSsnLycrcGF0aCsnLycpOgogICAgICAgICAgICAgICAgcHJvYmxlbXMuYXBwZW5kKGFycS5yc3BsaXQoJy4nLCAxKVswXS5zcGxpdCgnLScpWzBdLnN0cmlwKCkpCiAgICAgICAgI3ByaW50KHByb2JsZW1zKQogICAgICAgIHJldHVybiBwcm9ibGVtcwoKICAgIGRlZiBpbmZvUHJvYmxlbShzZWxmLCBpZCk6CiAgICAgICAgbWVudSA9IHNlbGYuZ2V0UGFnZSgnaHR0cHM6Ly93Li4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5tLmJyL2p1ZGdlLycrc2VsZi5sYW5nKycvcHJvYmxlbXMvdmlldy8nK2lkKS5maW5kKCJkaXYiLCB7ImlkIjogInByb2JsZW0tbWVudSJ9KQogICAgICAgICNwcmludCgiS0tLSy0tLS0iLCBtZW51KQogICAgICAgIHJldHVybiB7CiAgICAgICAgICAgICJsZXZlbCI6ICIxIiwKICAgICAgICAgICAgImNhdGVnb3J5IjogIkJlZ2lubmVyIgogICAgICAgIH0KCiAgICBkZWYgZ2V0Q29kZShzZWxmLCBpZCk6CiAgICAgICAgcHJpbnQoKHNlbGYuZ2V0UGFnZSgnaHR0cHM6Ly93Li4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi5tLmJyL2p1ZGdlLycrc2VsZi5sYW5nKycvcnVucy9jb2RlLycraWQpLmZpbmQoInByZSIsIHsiaWQiOiAiY29kZSJ9KSkpCgogICAgZGVmIGNsZWFuTmFtZShzZWxmLCBuYW1lKToKICAgICAgICByZXR1cm4gbmFtZS5yZXBsYWNlKCJcXCIsICIiKS5yZXBsYWNlKCIvIiwgIiIpLnJlcGxhY2UoIjoiLCAiIikucmVwbGFjZSgiPyIsICIiKS5yZXBsYWNlKCJcIiIsICIiKS5yZXBsYWNlKCI8IiwgIiIpLnJlcGxhY2UoIj4iLCAiIikucmVwbGFjZSgifCIsICIiKQoKICAgIGRlZiBkb3dubG9hZENvZGVzKHNlbGYsIHByb2JsZW1zKToKICAgICAgICBmb3IgaWQgaW4gcHJvYmxlbXM6CiAgICAgICAgICAgIGlmIHNlbGYubGFuZyA9PSAicHQiOgogICAgICAgICAgICAgICAgcHJpbnQoIlx0UHJvYmxlbWEgIitpZCsiIGJhaXhhZG8iKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcHJpbnQoIlx0UHJvYmxlbSAiK2lkKyIgZG93bmxvYWRlZCIpCiAgICAgICAgICAgIGluZm8gPSBzZWxmLmluZm9Qcm9ibGVtKGlkKQogICAgICAgICAgICBjb2RlID0gc2VsZi5nZXRDb2RlKHByb2JsZW1zW2lkXVsiaWRfY29kZSJdKQogICAgICAgICAgICBleHRlbnNpb24gPSAiIgogICAgICAgICAgICBjb21tZW50ID0gIi8vIgogICAgICAgICAgICBpZiBwcm9ibGVtc1tpZF1bImxhbmd1YWdlIl0gPT0gIkMrKyI6CiAgICAgICAgICAgICAgICBleHRlbnNpb24gPSAiLmNwcCIKICAgICAgICAgICAgZWxpZiBwcm9ibGVtc1tpZF1bImxhbmd1YWdlIl0gPT0gIkMiOgogICAgICAgICAgICAgICAgZXh0ZW5zaW9uID0gIi5jIgogICAgICAgICAgICBlbGlmIHByb2JsZW1zW2lkXVsibGFuZ3VhZ2UiXSA9PSAiSmF2YSI6CiAgICAgICAgICAgICAgICBleHRlbnNpb24gPSAiLmphdmEiCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBleHRlbnNpb24gPSAiLnB5IgogICAgICAgICAgICAgICAgY29tbWVudCA9ICIjIgoKICAgICAgICAgICAgYXJxID0gY29kZWNzLm9wZW4oIi4vVVJJLSIrc2VsZi5sYW5nLnVwcGVyKCkrIi8iK2luZm9bImNhdGVnb3J5Il0rIi8iK2lkKyIgLSAiK3NlbGYuY2xlYW5OYW1lKHByb2JsZW1zW2lkXVsibmFtZSJdKStleHRlbnNpb24sICJ3IiwgInV0Zi04IikKICAgICAgICAgICAgYXJxLndyaXRlKGNvbW1lbnQrc2VsZi5oZWFkZXJbMF0rIlxuIikKICAgICAgICAgICAgYXJxLndyaXRlKGNvbW1lbnQrc2VsZi5oZWFkZXJbMV0rcHJvYmxlbXNbaWRdWyJuYW1lIl0rIlxuIikKICAgICAgICAgICAgYXJxLndyaXRlKGNvbW1lbnQrc2VsZi5oZWFkZXJbMl0raW5mb1sibGV2ZWwiXSsiXG4iKQogICAgICAgICAgICBhcnEud3JpdGUoY29tbWVudCtzZWxmLmhlYWRlclszXStpbmZvWyJjYXRlZ29yeSJdKyJcbiIpCiAgICAgICAgICAgIGFycS53cml0ZShjb21tZW50KyIgVVJMOiBodHRwczovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLm0uYnIvanVkZ2UvIitzZWxmLmxhbmcrIi9wcm9ibGVtcy92aWV3LyIraWQrIlxuXG4iKQogICAgICAgICAgICBhcnEud3JpdGUoY29kZSkKICAgICAgICAgICAgYXJxLndyaXRlKCJcbiIpCiAgICAgICAgICAgIGFycS5jbG9zZSgpCiAgICAgICAgICAgIApleHRyYWN0b3IgPSBFeHRyYWN0b3IoKQoKbWVzc2FnZSA9IGV4dHJhY3Rvci5sb2dpbigpCndoaWxlIG1lc3NhZ2UgPT0gRmFsc2U6CiAgICBtZXNzYWdlID0gZXh0cmFjdG9yLmxvZ2luKCkKCnByaW50KG1lc3NhZ2VbMV0pCgpleHRyYWN0b3IuZm9sZGVyU3RydWN0dXJlKCkKcHJpbnQobWVzc2FnZVsyXSkKCnByaW50KG1lc3NhZ2VbM10pCmRvd25sb2FkX2xpc3QgPSBmaWx0ZXJQcm9ibGVtcyhleHRyYWN0b3IuZ2V0VXJpUHJvYmxlbXMoKSwgZXh0cmFjdG9yLmdldExvY2FsUHJvYmxlbXMoKSkKcHJpbnQobWVzc2FnZVs0XStzdHIobGVuKGRvd25sb2FkX2xpc3QpKSttZXNzYWdlWzVdKQoKcHJpbnQobWVzc2FnZVs2XSkKZXh0cmFjdG9yLmRvd25sb2FkQ29kZXMoZG93bmxvYWRfbGlzdCkKcHJpbnQobWVzc2FnZVs3XSk=