Google App Script, Mikrotik, Telegram dan VPNBook mulai memainkan kuartet

Hari ini dalam program: Di mana lagi Anda dapat menerapkan Skrip Google Apps jika ide normal berakhir. Otomasi kerja dengan VPNBook melalui serangkaian skrip dalam berbagai bahasa yang saya tidak tahu. Nedo-cURL oleh Mikrotik. Telegram melalui satu tempat, agar tidak berada di tempat lain, pengawasan diri memungkinkan.

Bagian 1. Tanpa Judul


Setahun yang lalu, saya menulis catatan " Hampir OCR untuk mendapatkan kata sandi VPNBook. PHP + Mikrotik " tentang cara mengatur pengambilan kata sandi otomatis di router Mikrotik untuk akses VPN gratis melalui VPNBook. Awal cerita ada di sana.

Sejak itu, banyak air telah mengalir, di Rusia mereka memblokir situs VPNBook, tetapi bukan server VPN publik itu sendiri, yang dipublikasikan di sana. Script PHP untuk mendekode gambar PNG kata sandi menjadi string teks sekarang juga dapat berfungsi ketika diluncurkan pada server yang lalu lintasnya tidak melewati sistem pemblokiran. Tetapi beberapa waktu lalu, bereksperimen dengan skrip layanan Google Apps Script (GAS ) .google.com , saya memutuskan untuk meninggalkan skrip PHP pada server web eksternal, menggantinya sebagian atau seluruhnya dengan skrip GAS yang berjalan sebagai Aplikasi Web (aplikasi web). Saya tidak memahami kebijakan eksekusi dan pembatasan GAS, tetapi semua yang saya lakukan berfungsi di akun Google gratis dan belum meminta uang. Saya tidak memiliki tujuan untuk menjelaskan Skrip Google Apps secara terperinci. GAS didasarkan pada bahasa JavaScript, Anda dapat menggunakan perpustakaan JS pihak ketiga, Anda dapat mempublikasikan skrip sebagai aplikasi web, yang dapat dibuat tersedia untuk semua orang tanpa otorisasi. Kemampuan implementasi GAS saat ini tidak cukup bagi saya, jadi saya harus keluar dan mencari solusi.

Awalnya saya memutuskan untuk menulis proxy untuk gambar PNG. Skrip web seharusnya meminta gambar kata sandi dari situs VPNBook (saya ingat bahwa kata sandi itu diterbitkan di sana dalam PNG) dan memberikannya kepada klien yang menyebut skrip ini untuk decoding. Cara seperti itu untuk berkeliling. Di sini pembatasan pertama GAS bertemu. Ternyata skrip tidak dapat merender gambar / png MIME, tetapi hanya format teks, JSON, TEXT, XML, dll. Tapi ada cara untuk mengatasi ini. Anda bisa menyandikan PNG ke Base64 dan mengembalikan string teks ke klien. Ada skrip serupa di Internet, misalnya techslides.com/image-proxy-with-google-app-scripts . Saya hanya menyederhanakan salah satunya. Saya hanya membutuhkan satu gambar dan hanya mengeluarkan string Base64. Hasilnya adalah skrip yang hanya terdiri dari satu fungsi doGet - pengendali permintaan GET yang mengembalikan string sebagai respons.

function doGet() { var response = UrlFetchApp.fetch('https://www.vpnbook.com/password.php'); var b64 = Utilities.base64Encode(response.getContent()); //var data = 'data:'+type+';base64,'+b64; return ContentService.createTextOutput(b64); } 

Contoh keluaran browser:

 iVBORw0KGgoAAAANSUhEUgAAAGQAAAANAQMAAABl11mFAAAABlBMVEX29vZMTExY89ZbAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAVUlEQVQImWNgIBrwSzCw/2ZgOADhSc5gYJCG8wxQedLdCcYFNXcgPHOZsxuSZxx7BuFZzsjdcJi34TBU5Y3cjc3IvM3McJ7kjNxtzDwwffwSIB7UTACt/h52C5DFqQAAAABJRU5ErkJggg== 

Selanjutnya adalah skrip PHP, yang dapat ditempatkan di server di dalam zona dengan penguncian sumber daya. Ini sangat mirip dengan skrip dari artikel sebelumnya, kecuali untuk perubahan kecil dalam parameter panggilan CURL. Anda harus mengizinkan cURL untuk mengakses HTTP / 1.1 302 Dipindahkan Sementara, karena GAS, ketika dipanggil, mengalihkan dari alamat skrip web ke alamat sementara dinamis:

 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 

Dan decoding Base64:

 $imgOCR = imagecreatefromstring(base64_decode($output)); 

Script itu sendiri
 <?php //   $wchar = 9; $hchar = 13; $strDict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 '; $imgDict = imagecreatetruecolor(2 + strlen($strDict)* $wchar, $hchar); $bg = imagecolorallocate($imgDict, 0xF6, 0xF6, 0xF6); $textcolor = imagecolorallocate($imgDict, 0x4C, 0x4C, 0x4C); imagefill($imgDict, 0, 0, $bg); imagestring($imgDict, 5, 2, 0, $strDict, $textcolor); //  cURL $ch = curl_init(); //  url,      //curl_setopt($ch, CURLOPT_URL, 'https://www.vpnbook.com/password.php'); curl_setopt($ch, CURLOPT_URL, 'https://script.google.com/macros/s/AKfycbwYPfaZobtjbFv0mSYI8U4NIXPh1Sft_DkGH8QKgg/exec'); //  ,      string curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); // also, this seems wise considering output is image. //   $output = curl_exec($ch); //  cURL curl_close($ch); // echo $output; $imgOCR = imagecreatefromstring(base64_decode($output)); //$imgOCR = imageCreateFromPng('password.png'); //      10  . 2 + 10*9 = 92 < 100 $maxchar = floor((imagesx($imgOCR) - 2) / 9); $imgBox = imagecreatetruecolor($wchar, $hchar); $hashDict = Array(); //   for ($k = 0; $k < strlen($strDict) ; $k++) { imagecopy($imgBox, $imgDict, 0, 0, 2 + $k * $wchar, 0, $wchar, $hchar); $hashStr = ""; for($y = 0; $y < $hchar ; $y++) for($x = 0; $x < $wchar; $x++) $hashStr .= (imagecolorat($imgBox, $x, $y) != 0xF6F6F6)? '1': '0'; $hashDict[$hashStr] = $strDict[$k]; } //     for ($k = 0; $k < $maxchar ; $k++) { imagecopy($imgBox, $imgOCR, 0, 0, 2 + $k * $wchar, 0, $wchar, $hchar); $hashStr = ""; for($y = 0; $y < $hchar ; $y++) for($x = 0; $x < $wchar; $x++) $hashStr .= (imagecolorat($imgBox, $x, $y) != 0xF6F6F6)? '1': '0'; $tempchar = $hashDict[$hashStr]; if ($tempchar=='u' || $tempchar=='y') //    $tempchar = (mt_rand(0, 1))? 'u': 'y'; //$tempchar = (time() / 60 % 60 % 2)? 'u': 'y'; elseif ($tempchar==' ') break; print($tempchar); } /* header('Content-type: image/png'); imagepng($imgDict); */ //var_dump($hashDict); imagedestroy($imgDict); imagedestroy($imgOCR); imagedestroy($imgBox); ?> 


Skrip PHP ini menerjemahkan sandi PNG dan mengembalikannya sebagai string teks. Selanjutnya, seperti pada artikel pertama di bagian tentang Mikrotik. Router mengambil kata sandi menggunakan fetch.
Hasilnya adalah skema kerja 2 layanan perantara di depan Mikrotik.

Bagian 2. Dorong GAS. Singkirkan skrip dekoder PHP


Selama percobaan dengan GAS, muncul ide untuk meninggalkan decoder kata sandi dalam PHP, menulis ulang dalam GAS. Dan di sini ditemukan masalah besar: Google script tidak memiliki fungsi pemrosesan PNG, satu-satunya hal yang dapat dilakukan adalah mengubah PNG menjadi array byte. Tidak ada pertanyaan tentang manipulasi dengan bagian gambar dan piksel. Saya naik ke Github mencari perpustakaan JS untuk bekerja dengan PNG, saya menemukan banyak dari mereka: PNG.js, UPNG.js, pngjs. Beberapa tidak mendukung kedalaman warna 1-bit piksel PNG (gambar dengan kata sandi). Mereka menarik berbagai pustaka kompresi zlib. Secara umum, itu semua tampak sedikit rumit bagi saya, dan saya memutuskan untuk menulis sendiri konverter primitif hanya untuk gambar PNG saya ke bitmap dengan fungsi mengakses piksel dengan koordinat XY. Kemudian datang perendaman lengkap dalam format PNG: hex editor, standar membaca, banyak deskripsi di jaringan. Dan akhirnya, saya berlari ke bagian PNG dari file IDAT, dikemas dengan zlib, yang berisi array piksel.

Untuk itu diperlukan fungsi unpacking zlib, yang tentu saja tidak ada di GAS. Anehnya, mereka memiliki gzip / ungzip dan zip / unzip, tetapi tidak ada zlib. Setelah membaca tentang gzip (tingkat kedua perendaman setelah format PNG), saya sampai pada kesimpulan bahwa tidak mungkin untuk merakit "sepeda" dalam bentuk arsip kuasi-gzip dari bagian IDAT, meskipun kompresi zlib digunakan di sana-sini. Karena Untuk membangun arsip gzip yang valid, Anda perlu mengetahui panjang data yang sudah dibongkar, yang tidak bisa saya dapatkan tanpa membukanya :) Dan dengan panjang yang salah, GAS menganggap arsip rusak. Pada akhirnya, saya menoleh ke Github dan menemukan solusi hebat: zlib.js untuk pustaka Skrip Google Apps (https://github.com/hinimub/zlib.js/blob/develop/README.en.md). Yang secara khusus disiapkan untuk integrasi ke dalam proyek GAS melalui perpustakaan kunci proyek. Kemudian teka-teki mulai menyatu. Setelah menulis dekompresi array piksel dan fungsi untuk mengakses koordinat piksel XY, dimungkinkan untuk mentransfer skrip dekoder dari PHP ke GAS.

Dihitung secara terpisah tabel hash dari kamus karakter kata sandi yang mungkin. Ini adalah tindakan satu kali yang saya lakukan dalam program pihak ketiga (di LabVIEW, halo, kolega). Setiap karakter dalam gambar dapat dialokasikan sebagai 8 bit (tanpa indentasi) x 10 baris. 1 byte sudah cukup untuk menyandikan 8 piksel dari satu baris karakter. Anda dapat menyimpan string piksel dalam seluruh angka (byte), dan seluruh karakter sebagai urutan 10 byte. Ternyata 10 angka heks per karakter. Selanjutnya, decoder GAS mengulangi nenek moyang PHP-nya.

Hasilnya adalah skrip yang berfungsi sepenuhnya dalam GAS.
 function doGet() { //var file = DriveApp.getFilesByName("password2.png").next(); //var image = file.getBlob(); var image = UrlFetchApp.fetch('https://www.vpnbook.com/password.php').getBlob(); var imageString = image.getDataAsString(); var imageArray = image.getBytes().map(function(e) { return e & 0xff; }); // imageArray = blobToUint8(imageArray); var chunkIDATStart = imageString.indexOf("IDAT") + 4; //   IDAT var chunkIDATLen = bytesToUint32(imageArray, imageString.indexOf("IDAT") - 4); //   IDAT var IDATArray = imageArray.slice(chunkIDATStart, chunkIDATStart + chunkIDATLen) var inflate = new zlibjs.Inflate(IDATArray); //  IDAT   zlib var plain = inflate.decompress(); const Width = 100; //   const Height = 13; //   const wchar = 9; //   const hchar = 13; //   //Logger.log(typeof(plain)); //Logger.log(plain); rowlen = (Width / 8) >> 0; //   ,    if ((Width - rowlen * 8) > 0) { //    rowlen+=2; // +1 filter byte at the beginning of each row.    PNG } else { rowlen++; } function getXY(x, y) { //  : 0/1 var xbyte = (x / 8 >> 0); //  ,   //Logger.log("xbyte: " + xbyte); var xbit = x - xbyte * 8; //  ,    //Logger.log("xbit: " + xbit); return (plain[xbyte + 1 + y * rowlen] << xbit & 0x80) >> 7; // +1 filter byte at the beginning of each row } // Logger.log("getXY: " + getXY(4, 3)); // - .        8  (  ) x 10 ,        (byte),      10  . //  10 hex   . var hashDict = {'183C66C3C3C3FFC3C3C3':'A','FCC6C3C6FCC6C3C3C6FC':'B','3E63C1C0C0C0C0C1633E':'C','FCC6C3C3C3C3C3C3C6FC':'D', 'FEC0C0C0FCC0C0C0C0FE':'E','FFC0C0C0FCC0C0C0C0C0':'F','3E63C0C0C0C7C3C3633E':'G','C3C3C3C3FFC3C3C3C3C3':'H', '7E18181818181818187E':'I','1E666666466C38':'J','C3C6CCD8F0F0D8CCC6C3':'K','C0C0C0C0C0C0C0C0C0FE':'L', 'C3E7FFDBDBDBC3C3C3C3':'M','C3E3F3F3DBDBCFC7C7C3':'N','3C66C3C3C3C3C3C3663C':'O','FEC3C3C3FEC0C0C0C0C0':'P', '3C66C3C3C3C3DBCF663D':'Q','FEC3C3C3FEF8CCC6C3C3':'R','7EC3C0C07E333C37E':'S','FF181818181818181818':'T', 'C3C3C3C3C3C3C3C3663C':'U','C3C3C36666663C3C1818':'V','C3C3C3C3DBDBDBFFE7C3':'W','C3C3663C18183C66C3C3':'X', 'C3C3663C181818181818':'Y','FE66C183060C0C0FE':'Z','0003E6337FC3C77B':'a','C0C0C0DCE6C3C3C3E6DC':'b', '0003E63C0C0C0633E':'c','3333B67C3C3C3673B':'d','0003C66C3FFC0633E':'e','1E33333030FC30303030':'f', '0007DC7C6C67CC07E':'g','C0C0C0DCE6C3C3C3C3C3':'h','181803818181818187E':'i','660E66666C6':'j', '606060666C78786C6663':'k','3818181818181818183C':'l','000B6DBDBDBDBDBDB':'m','000DCE6C3C3C3C3C3':'n', '0003C66C3C3C3663C':'o','000DCE6C3C3C3E6DC':'p','0003B67C3C3C3673B':'q','000DE736060606060':'r', '0007EC3C07E3C37E':'s','03030FC30303030331E':'t','000C3C3C3C3C3673B':'u','000C3C366663C3C18':'v', '000C3C3DBDBDBFF66':'w','000C3663C183C66C3':'x','000C3C3C3C3C3673B':'y','0007E6C1830607E':'z', '183C66C3C3C3C3663C18':'0','1838781818181818187E':'1','3C66C336C183060FF':'2','7CC6361C633C67C':'3', '6E1E3666C6FF666':'4','FEC0C0DCE633C3663C':'5','3C66C2C0DCE6C3C3663C':'6','FF336C183060C0C0':'7', '3C66C3663C66C3C3663C':'8','3C66C3C3673B343663C':'9','0000000000':' '}; //      10  . 2 + 10*9 = 92 < 100 const maxchar = (Width - 2) / wchar >> 0; var password = ''; for (var charX = 2; charX < maxchar * wchar + 2; charX+=wchar) { //    var hash = ''; //   for (var charY = 3; charY < hchar; charY++) { //   Y- var charrow = 0; //   -  8   for (var charXbit = 0; charXbit < 8; charXbit++) { //   X- charrow <<= 1; charrow |= getXY(charX + charXbit, charY); } hash += charrow.toString(16).toUpperCase(); //Logger.log("charrow: " + charrow.toString(2)); //Logger.log("charrow: " + charrow.toString(16).toUpperCase()); } var tempChar = hashDict[hash]; if (tempChar === 'u' || tempChar === 'y') { //     tempChar = (Date.now() % 2) ? 'u': 'y'; } if (tempChar !== ' ') { password += tempChar; // Logger.log("hash: " + hash); // Logger.log("Char: " + tempChar); } } Logger.log("password: " + password); return ContentService.createTextOutput(password); } function blobToUint8(blob) { return blob.map(function(e){ return e & 0xff; }); } function bytesToUint32(byteArray, start) { var value = 0; for (var i = start; i < start + 4; i++) { value = (value * 256) + (byteArray[i] & 0xff); } return value; } function my2() { var file = DriveApp.getFilesByName("password2.png").next(); // var file = DriveApp.getFilesByName("test.bin").next(); var image = file.getBlob(); //var imageArray = image.getBytes(); //var img = UrlFetchApp.fetch('http://example.com/image.png'); var reader = new pngjs.PNGReader(image.getBytes()); var png = reader.parse(function(err, png){ if (err) throw err; return png; }); Logger.log(png); } 'A', 'FCC6C3C6FCC6C3C3C6FC': 'B', '3E63C1C0C0C0C0C1633E': 'C', 'FCC6C3C3C3C3C3C3C6FC': 'D', 'FEC0C0C0FCC0C0C0C0FE': 'E', 'FFC0C0C0FCC0C0C0C0C0': 'F function doGet() { //var file = DriveApp.getFilesByName("password2.png").next(); //var image = file.getBlob(); var image = UrlFetchApp.fetch('https://www.vpnbook.com/password.php').getBlob(); var imageString = image.getDataAsString(); var imageArray = image.getBytes().map(function(e) { return e & 0xff; }); // imageArray = blobToUint8(imageArray); var chunkIDATStart = imageString.indexOf("IDAT") + 4; //   IDAT var chunkIDATLen = bytesToUint32(imageArray, imageString.indexOf("IDAT") - 4); //   IDAT var IDATArray = imageArray.slice(chunkIDATStart, chunkIDATStart + chunkIDATLen) var inflate = new zlibjs.Inflate(IDATArray); //  IDAT   zlib var plain = inflate.decompress(); const Width = 100; //   const Height = 13; //   const wchar = 9; //   const hchar = 13; //   //Logger.log(typeof(plain)); //Logger.log(plain); rowlen = (Width / 8) >> 0; //   ,    if ((Width - rowlen * 8) > 0) { //    rowlen+=2; // +1 filter byte at the beginning of each row.    PNG } else { rowlen++; } function getXY(x, y) { //  : 0/1 var xbyte = (x / 8 >> 0); //  ,   //Logger.log("xbyte: " + xbyte); var xbit = x - xbyte * 8; //  ,    //Logger.log("xbit: " + xbit); return (plain[xbyte + 1 + y * rowlen] << xbit & 0x80) >> 7; // +1 filter byte at the beginning of each row } // Logger.log("getXY: " + getXY(4, 3)); // - .        8  (  ) x 10 ,        (byte),      10  . //  10 hex   . var hashDict = {'183C66C3C3C3FFC3C3C3':'A','FCC6C3C6FCC6C3C3C6FC':'B','3E63C1C0C0C0C0C1633E':'C','FCC6C3C3C3C3C3C3C6FC':'D', 'FEC0C0C0FCC0C0C0C0FE':'E','FFC0C0C0FCC0C0C0C0C0':'F','3E63C0C0C0C7C3C3633E':'G','C3C3C3C3FFC3C3C3C3C3':'H', '7E18181818181818187E':'I','1E666666466C38':'J','C3C6CCD8F0F0D8CCC6C3':'K','C0C0C0C0C0C0C0C0C0FE':'L', 'C3E7FFDBDBDBC3C3C3C3':'M','C3E3F3F3DBDBCFC7C7C3':'N','3C66C3C3C3C3C3C3663C':'O','FEC3C3C3FEC0C0C0C0C0':'P', '3C66C3C3C3C3DBCF663D':'Q','FEC3C3C3FEF8CCC6C3C3':'R','7EC3C0C07E333C37E':'S','FF181818181818181818':'T', 'C3C3C3C3C3C3C3C3663C':'U','C3C3C36666663C3C1818':'V','C3C3C3C3DBDBDBFFE7C3':'W','C3C3663C18183C66C3C3':'X', 'C3C3663C181818181818':'Y','FE66C183060C0C0FE':'Z','0003E6337FC3C77B':'a','C0C0C0DCE6C3C3C3E6DC':'b', '0003E63C0C0C0633E':'c','3333B67C3C3C3673B':'d','0003C66C3FFC0633E':'e','1E33333030FC30303030':'f', '0007DC7C6C67CC07E':'g','C0C0C0DCE6C3C3C3C3C3':'h','181803818181818187E':'i','660E66666C6':'j', '606060666C78786C6663':'k','3818181818181818183C':'l','000B6DBDBDBDBDBDB':'m','000DCE6C3C3C3C3C3':'n', '0003C66C3C3C3663C':'o','000DCE6C3C3C3E6DC':'p','0003B67C3C3C3673B':'q','000DE736060606060':'r', '0007EC3C07E3C37E':'s','03030FC30303030331E':'t','000C3C3C3C3C3673B':'u','000C3C366663C3C18':'v', '000C3C3DBDBDBFF66':'w','000C3663C183C66C3':'x','000C3C3C3C3C3673B':'y','0007E6C1830607E':'z', '183C66C3C3C3C3663C18':'0','1838781818181818187E':'1','3C66C336C183060FF':'2','7CC6361C633C67C':'3', '6E1E3666C6FF666':'4','FEC0C0DCE633C3663C':'5','3C66C2C0DCE6C3C3663C':'6','FF336C183060C0C0':'7', '3C66C3663C66C3C3663C':'8','3C66C3C3673B343663C':'9','0000000000':' '}; //      10  . 2 + 10*9 = 92 < 100 const maxchar = (Width - 2) / wchar >> 0; var password = ''; for (var charX = 2; charX < maxchar * wchar + 2; charX+=wchar) { //    var hash = ''; //   for (var charY = 3; charY < hchar; charY++) { //   Y- var charrow = 0; //   -  8   for (var charXbit = 0; charXbit < 8; charXbit++) { //   X- charrow <<= 1; charrow |= getXY(charX + charXbit, charY); } hash += charrow.toString(16).toUpperCase(); //Logger.log("charrow: " + charrow.toString(2)); //Logger.log("charrow: " + charrow.toString(16).toUpperCase()); } var tempChar = hashDict[hash]; if (tempChar === 'u' || tempChar === 'y') { //     tempChar = (Date.now() % 2) ? 'u': 'y'; } if (tempChar !== ' ') { password += tempChar; // Logger.log("hash: " + hash); // Logger.log("Char: " + tempChar); } } Logger.log("password: " + password); return ContentService.createTextOutput(password); } function blobToUint8(blob) { return blob.map(function(e){ return e & 0xff; }); } function bytesToUint32(byteArray, start) { var value = 0; for (var i = start; i < start + 4; i++) { value = (value * 256) + (byteArray[i] & 0xff); } return value; } function my2() { var file = DriveApp.getFilesByName("password2.png").next(); // var file = DriveApp.getFilesByName("test.bin").next(); var image = file.getBlob(); //var imageArray = image.getBytes(); //var img = UrlFetchApp.fetch('http://example.com/image.png'); var reader = new pngjs.PNGReader(image.getBytes()); var png = reader.parse(function(err, png){ if (err) throw err; return png; }); Logger.log(png); } '' C3C3C3C3FFC3C3C3C3C3 ':' H '' 7E18181818181818187E ':' aku '' 1E666666466C38 ':' J '' C3C6CCD8F0F0D8CCC6C3 ':' K '' C0C0C0C0C0C0C0C0C0FE ':' L', function doGet() { //var file = DriveApp.getFilesByName("password2.png").next(); //var image = file.getBlob(); var image = UrlFetchApp.fetch('https://www.vpnbook.com/password.php').getBlob(); var imageString = image.getDataAsString(); var imageArray = image.getBytes().map(function(e) { return e & 0xff; }); // imageArray = blobToUint8(imageArray); var chunkIDATStart = imageString.indexOf("IDAT") + 4; //   IDAT var chunkIDATLen = bytesToUint32(imageArray, imageString.indexOf("IDAT") - 4); //   IDAT var IDATArray = imageArray.slice(chunkIDATStart, chunkIDATStart + chunkIDATLen) var inflate = new zlibjs.Inflate(IDATArray); //  IDAT   zlib var plain = inflate.decompress(); const Width = 100; //   const Height = 13; //   const wchar = 9; //   const hchar = 13; //   //Logger.log(typeof(plain)); //Logger.log(plain); rowlen = (Width / 8) >> 0; //   ,    if ((Width - rowlen * 8) > 0) { //    rowlen+=2; // +1 filter byte at the beginning of each row.    PNG } else { rowlen++; } function getXY(x, y) { //  : 0/1 var xbyte = (x / 8 >> 0); //  ,   //Logger.log("xbyte: " + xbyte); var xbit = x - xbyte * 8; //  ,    //Logger.log("xbit: " + xbit); return (plain[xbyte + 1 + y * rowlen] << xbit & 0x80) >> 7; // +1 filter byte at the beginning of each row } // Logger.log("getXY: " + getXY(4, 3)); // - .        8  (  ) x 10 ,        (byte),      10  . //  10 hex   . var hashDict = {'183C66C3C3C3FFC3C3C3':'A','FCC6C3C6FCC6C3C3C6FC':'B','3E63C1C0C0C0C0C1633E':'C','FCC6C3C3C3C3C3C3C6FC':'D', 'FEC0C0C0FCC0C0C0C0FE':'E','FFC0C0C0FCC0C0C0C0C0':'F','3E63C0C0C0C7C3C3633E':'G','C3C3C3C3FFC3C3C3C3C3':'H', '7E18181818181818187E':'I','1E666666466C38':'J','C3C6CCD8F0F0D8CCC6C3':'K','C0C0C0C0C0C0C0C0C0FE':'L', 'C3E7FFDBDBDBC3C3C3C3':'M','C3E3F3F3DBDBCFC7C7C3':'N','3C66C3C3C3C3C3C3663C':'O','FEC3C3C3FEC0C0C0C0C0':'P', '3C66C3C3C3C3DBCF663D':'Q','FEC3C3C3FEF8CCC6C3C3':'R','7EC3C0C07E333C37E':'S','FF181818181818181818':'T', 'C3C3C3C3C3C3C3C3663C':'U','C3C3C36666663C3C1818':'V','C3C3C3C3DBDBDBFFE7C3':'W','C3C3663C18183C66C3C3':'X', 'C3C3663C181818181818':'Y','FE66C183060C0C0FE':'Z','0003E6337FC3C77B':'a','C0C0C0DCE6C3C3C3E6DC':'b', '0003E63C0C0C0633E':'c','3333B67C3C3C3673B':'d','0003C66C3FFC0633E':'e','1E33333030FC30303030':'f', '0007DC7C6C67CC07E':'g','C0C0C0DCE6C3C3C3C3C3':'h','181803818181818187E':'i','660E66666C6':'j', '606060666C78786C6663':'k','3818181818181818183C':'l','000B6DBDBDBDBDBDB':'m','000DCE6C3C3C3C3C3':'n', '0003C66C3C3C3663C':'o','000DCE6C3C3C3E6DC':'p','0003B67C3C3C3673B':'q','000DE736060606060':'r', '0007EC3C07E3C37E':'s','03030FC30303030331E':'t','000C3C3C3C3C3673B':'u','000C3C366663C3C18':'v', '000C3C3DBDBDBFF66':'w','000C3663C183C66C3':'x','000C3C3C3C3C3673B':'y','0007E6C1830607E':'z', '183C66C3C3C3C3663C18':'0','1838781818181818187E':'1','3C66C336C183060FF':'2','7CC6361C633C67C':'3', '6E1E3666C6FF666':'4','FEC0C0DCE633C3663C':'5','3C66C2C0DCE6C3C3663C':'6','FF336C183060C0C0':'7', '3C66C3663C66C3C3663C':'8','3C66C3C3673B343663C':'9','0000000000':' '}; //      10  . 2 + 10*9 = 92 < 100 const maxchar = (Width - 2) / wchar >> 0; var password = ''; for (var charX = 2; charX < maxchar * wchar + 2; charX+=wchar) { //    var hash = ''; //   for (var charY = 3; charY < hchar; charY++) { //   Y- var charrow = 0; //   -  8   for (var charXbit = 0; charXbit < 8; charXbit++) { //   X- charrow <<= 1; charrow |= getXY(charX + charXbit, charY); } hash += charrow.toString(16).toUpperCase(); //Logger.log("charrow: " + charrow.toString(2)); //Logger.log("charrow: " + charrow.toString(16).toUpperCase()); } var tempChar = hashDict[hash]; if (tempChar === 'u' || tempChar === 'y') { //     tempChar = (Date.now() % 2) ? 'u': 'y'; } if (tempChar !== ' ') { password += tempChar; // Logger.log("hash: " + hash); // Logger.log("Char: " + tempChar); } } Logger.log("password: " + password); return ContentService.createTextOutput(password); } function blobToUint8(blob) { return blob.map(function(e){ return e & 0xff; }); } function bytesToUint32(byteArray, start) { var value = 0; for (var i = start; i < start + 4; i++) { value = (value * 256) + (byteArray[i] & 0xff); } return value; } function my2() { var file = DriveApp.getFilesByName("password2.png").next(); // var file = DriveApp.getFilesByName("test.bin").next(); var image = file.getBlob(); //var imageArray = image.getBytes(); //var img = UrlFetchApp.fetch('http://example.com/image.png'); var reader = new pngjs.PNGReader(image.getBytes()); var png = reader.parse(function(err, png){ if (err) throw err; return png; }); Logger.log(png); } 'C3E3F3F3DBDBCFC7C7C3': 'N', '3C66C3C3C3C3C3C3663C': 'O', 'FEC3C3C3FEC0C0C0C0C0': 'P', '3C66C3C3C3C3DBCF663D': 'Q', 'FEC3C3C3FEF8CCC6C3C3': 'R', '7EC3C0C07E333C37E function doGet() { //var file = DriveApp.getFilesByName("password2.png").next(); //var image = file.getBlob(); var image = UrlFetchApp.fetch('https://www.vpnbook.com/password.php').getBlob(); var imageString = image.getDataAsString(); var imageArray = image.getBytes().map(function(e) { return e & 0xff; }); // imageArray = blobToUint8(imageArray); var chunkIDATStart = imageString.indexOf("IDAT") + 4; //   IDAT var chunkIDATLen = bytesToUint32(imageArray, imageString.indexOf("IDAT") - 4); //   IDAT var IDATArray = imageArray.slice(chunkIDATStart, chunkIDATStart + chunkIDATLen) var inflate = new zlibjs.Inflate(IDATArray); //  IDAT   zlib var plain = inflate.decompress(); const Width = 100; //   const Height = 13; //   const wchar = 9; //   const hchar = 13; //   //Logger.log(typeof(plain)); //Logger.log(plain); rowlen = (Width / 8) >> 0; //   ,    if ((Width - rowlen * 8) > 0) { //    rowlen+=2; // +1 filter byte at the beginning of each row.    PNG } else { rowlen++; } function getXY(x, y) { //  : 0/1 var xbyte = (x / 8 >> 0); //  ,   //Logger.log("xbyte: " + xbyte); var xbit = x - xbyte * 8; //  ,    //Logger.log("xbit: " + xbit); return (plain[xbyte + 1 + y * rowlen] << xbit & 0x80) >> 7; // +1 filter byte at the beginning of each row } // Logger.log("getXY: " + getXY(4, 3)); // - .        8  (  ) x 10 ,        (byte),      10  . //  10 hex   . var hashDict = {'183C66C3C3C3FFC3C3C3':'A','FCC6C3C6FCC6C3C3C6FC':'B','3E63C1C0C0C0C0C1633E':'C','FCC6C3C3C3C3C3C3C6FC':'D', 'FEC0C0C0FCC0C0C0C0FE':'E','FFC0C0C0FCC0C0C0C0C0':'F','3E63C0C0C0C7C3C3633E':'G','C3C3C3C3FFC3C3C3C3C3':'H', '7E18181818181818187E':'I','1E666666466C38':'J','C3C6CCD8F0F0D8CCC6C3':'K','C0C0C0C0C0C0C0C0C0FE':'L', 'C3E7FFDBDBDBC3C3C3C3':'M','C3E3F3F3DBDBCFC7C7C3':'N','3C66C3C3C3C3C3C3663C':'O','FEC3C3C3FEC0C0C0C0C0':'P', '3C66C3C3C3C3DBCF663D':'Q','FEC3C3C3FEF8CCC6C3C3':'R','7EC3C0C07E333C37E':'S','FF181818181818181818':'T', 'C3C3C3C3C3C3C3C3663C':'U','C3C3C36666663C3C1818':'V','C3C3C3C3DBDBDBFFE7C3':'W','C3C3663C18183C66C3C3':'X', 'C3C3663C181818181818':'Y','FE66C183060C0C0FE':'Z','0003E6337FC3C77B':'a','C0C0C0DCE6C3C3C3E6DC':'b', '0003E63C0C0C0633E':'c','3333B67C3C3C3673B':'d','0003C66C3FFC0633E':'e','1E33333030FC30303030':'f', '0007DC7C6C67CC07E':'g','C0C0C0DCE6C3C3C3C3C3':'h','181803818181818187E':'i','660E66666C6':'j', '606060666C78786C6663':'k','3818181818181818183C':'l','000B6DBDBDBDBDBDB':'m','000DCE6C3C3C3C3C3':'n', '0003C66C3C3C3663C':'o','000DCE6C3C3C3E6DC':'p','0003B67C3C3C3673B':'q','000DE736060606060':'r', '0007EC3C07E3C37E':'s','03030FC30303030331E':'t','000C3C3C3C3C3673B':'u','000C3C366663C3C18':'v', '000C3C3DBDBDBFF66':'w','000C3663C183C66C3':'x','000C3C3C3C3C3673B':'y','0007E6C1830607E':'z', '183C66C3C3C3C3663C18':'0','1838781818181818187E':'1','3C66C336C183060FF':'2','7CC6361C633C67C':'3', '6E1E3666C6FF666':'4','FEC0C0DCE633C3663C':'5','3C66C2C0DCE6C3C3663C':'6','FF336C183060C0C0':'7', '3C66C3663C66C3C3663C':'8','3C66C3C3673B343663C':'9','0000000000':' '}; //      10  . 2 + 10*9 = 92 < 100 const maxchar = (Width - 2) / wchar >> 0; var password = ''; for (var charX = 2; charX < maxchar * wchar + 2; charX+=wchar) { //    var hash = ''; //   for (var charY = 3; charY < hchar; charY++) { //   Y- var charrow = 0; //   -  8   for (var charXbit = 0; charXbit < 8; charXbit++) { //   X- charrow <<= 1; charrow |= getXY(charX + charXbit, charY); } hash += charrow.toString(16).toUpperCase(); //Logger.log("charrow: " + charrow.toString(2)); //Logger.log("charrow: " + charrow.toString(16).toUpperCase()); } var tempChar = hashDict[hash]; if (tempChar === 'u' || tempChar === 'y') { //     tempChar = (Date.now() % 2) ? 'u': 'y'; } if (tempChar !== ' ') { password += tempChar; // Logger.log("hash: " + hash); // Logger.log("Char: " + tempChar); } } Logger.log("password: " + password); return ContentService.createTextOutput(password); } function blobToUint8(blob) { return blob.map(function(e){ return e & 0xff; }); } function bytesToUint32(byteArray, start) { var value = 0; for (var i = start; i < start + 4; i++) { value = (value * 256) + (byteArray[i] & 0xff); } return value; } function my2() { var file = DriveApp.getFilesByName("password2.png").next(); // var file = DriveApp.getFilesByName("test.bin").next(); var image = file.getBlob(); //var imageArray = image.getBytes(); //var img = UrlFetchApp.fetch('http://example.com/image.png'); var reader = new pngjs.PNGReader(image.getBytes()); var png = reader.parse(function(err, png){ if (err) throw err; return png; }); Logger.log(png); } 000B6DBDBDBDBDBDB ':' m '' 000DCE6C3C3C3C3C3 ':' n '' 0003C66C3C3C3663C ':' o '' 000DCE6C3C3C3E6DC ':' p '' 0003B67C3C3C3673B ':' q '' 000DE736060606060 ' function doGet() { //var file = DriveApp.getFilesByName("password2.png").next(); //var image = file.getBlob(); var image = UrlFetchApp.fetch('https://www.vpnbook.com/password.php').getBlob(); var imageString = image.getDataAsString(); var imageArray = image.getBytes().map(function(e) { return e & 0xff; }); // imageArray = blobToUint8(imageArray); var chunkIDATStart = imageString.indexOf("IDAT") + 4; //   IDAT var chunkIDATLen = bytesToUint32(imageArray, imageString.indexOf("IDAT") - 4); //   IDAT var IDATArray = imageArray.slice(chunkIDATStart, chunkIDATStart + chunkIDATLen) var inflate = new zlibjs.Inflate(IDATArray); //  IDAT   zlib var plain = inflate.decompress(); const Width = 100; //   const Height = 13; //   const wchar = 9; //   const hchar = 13; //   //Logger.log(typeof(plain)); //Logger.log(plain); rowlen = (Width / 8) >> 0; //   ,    if ((Width - rowlen * 8) > 0) { //    rowlen+=2; // +1 filter byte at the beginning of each row.    PNG } else { rowlen++; } function getXY(x, y) { //  : 0/1 var xbyte = (x / 8 >> 0); //  ,   //Logger.log("xbyte: " + xbyte); var xbit = x - xbyte * 8; //  ,    //Logger.log("xbit: " + xbit); return (plain[xbyte + 1 + y * rowlen] << xbit & 0x80) >> 7; // +1 filter byte at the beginning of each row } // Logger.log("getXY: " + getXY(4, 3)); // - .        8  (  ) x 10 ,        (byte),      10  . //  10 hex   . var hashDict = {'183C66C3C3C3FFC3C3C3':'A','FCC6C3C6FCC6C3C3C6FC':'B','3E63C1C0C0C0C0C1633E':'C','FCC6C3C3C3C3C3C3C6FC':'D', 'FEC0C0C0FCC0C0C0C0FE':'E','FFC0C0C0FCC0C0C0C0C0':'F','3E63C0C0C0C7C3C3633E':'G','C3C3C3C3FFC3C3C3C3C3':'H', '7E18181818181818187E':'I','1E666666466C38':'J','C3C6CCD8F0F0D8CCC6C3':'K','C0C0C0C0C0C0C0C0C0FE':'L', 'C3E7FFDBDBDBC3C3C3C3':'M','C3E3F3F3DBDBCFC7C7C3':'N','3C66C3C3C3C3C3C3663C':'O','FEC3C3C3FEC0C0C0C0C0':'P', '3C66C3C3C3C3DBCF663D':'Q','FEC3C3C3FEF8CCC6C3C3':'R','7EC3C0C07E333C37E':'S','FF181818181818181818':'T', 'C3C3C3C3C3C3C3C3663C':'U','C3C3C36666663C3C1818':'V','C3C3C3C3DBDBDBFFE7C3':'W','C3C3663C18183C66C3C3':'X', 'C3C3663C181818181818':'Y','FE66C183060C0C0FE':'Z','0003E6337FC3C77B':'a','C0C0C0DCE6C3C3C3E6DC':'b', '0003E63C0C0C0633E':'c','3333B67C3C3C3673B':'d','0003C66C3FFC0633E':'e','1E33333030FC30303030':'f', '0007DC7C6C67CC07E':'g','C0C0C0DCE6C3C3C3C3C3':'h','181803818181818187E':'i','660E66666C6':'j', '606060666C78786C6663':'k','3818181818181818183C':'l','000B6DBDBDBDBDBDB':'m','000DCE6C3C3C3C3C3':'n', '0003C66C3C3C3663C':'o','000DCE6C3C3C3E6DC':'p','0003B67C3C3C3673B':'q','000DE736060606060':'r', '0007EC3C07E3C37E':'s','03030FC30303030331E':'t','000C3C3C3C3C3673B':'u','000C3C366663C3C18':'v', '000C3C3DBDBDBFF66':'w','000C3663C183C66C3':'x','000C3C3C3C3C3673B':'y','0007E6C1830607E':'z', '183C66C3C3C3C3663C18':'0','1838781818181818187E':'1','3C66C336C183060FF':'2','7CC6361C633C67C':'3', '6E1E3666C6FF666':'4','FEC0C0DCE633C3663C':'5','3C66C2C0DCE6C3C3663C':'6','FF336C183060C0C0':'7', '3C66C3663C66C3C3663C':'8','3C66C3C3673B343663C':'9','0000000000':' '}; //      10  . 2 + 10*9 = 92 < 100 const maxchar = (Width - 2) / wchar >> 0; var password = ''; for (var charX = 2; charX < maxchar * wchar + 2; charX+=wchar) { //    var hash = ''; //   for (var charY = 3; charY < hchar; charY++) { //   Y- var charrow = 0; //   -  8   for (var charXbit = 0; charXbit < 8; charXbit++) { //   X- charrow <<= 1; charrow |= getXY(charX + charXbit, charY); } hash += charrow.toString(16).toUpperCase(); //Logger.log("charrow: " + charrow.toString(2)); //Logger.log("charrow: " + charrow.toString(16).toUpperCase()); } var tempChar = hashDict[hash]; if (tempChar === 'u' || tempChar === 'y') { //     tempChar = (Date.now() % 2) ? 'u': 'y'; } if (tempChar !== ' ') { password += tempChar; // Logger.log("hash: " + hash); // Logger.log("Char: " + tempChar); } } Logger.log("password: " + password); return ContentService.createTextOutput(password); } function blobToUint8(blob) { return blob.map(function(e){ return e & 0xff; }); } function bytesToUint32(byteArray, start) { var value = 0; for (var i = start; i < start + 4; i++) { value = (value * 256) + (byteArray[i] & 0xff); } return value; } function my2() { var file = DriveApp.getFilesByName("password2.png").next(); // var file = DriveApp.getFilesByName("test.bin").next(); var image = file.getBlob(); //var imageArray = image.getBytes(); //var img = UrlFetchApp.fetch('http://example.com/image.png'); var reader = new pngjs.PNGReader(image.getBytes()); var png = reader.parse(function(err, png){ if (err) throw err; return png; }); Logger.log(png); } 


Script hanya mengimplementasikan metode GET. Saat menjalankan permintaan GET untuk skrip ini, yang diterbitkan sebagai Aplikasi Web, respons akan segera berisi kata sandi yang didekodekan dalam bentuk string.

Bagian 3. Mikrotik dan Pindah Sementara 302


Jadi, kami memiliki skrip yang berjalan di server Aplikasi Web eksternal, yang tidak tergantung pada kunci dan mengembalikan kata sandi teks biasa. Dan sepertinya tidak ada yang lebih mudah daripada memintanya dengan mengambil perintah di RouterOS Mikrotik. Tapi kemudian kejutan lain menunggu saya. Menanggapi permintaan (alamat asli diubah), ambil pengembalian "302 Pindah Sementara".

 [admin@MikroTik] /environment> :put ([/tool fetch url="https://script.google.com/macros/s/A.....A/exec" http-method=get output=user as-value]->"data") failure: closing connection: <302 Moved Temporarily "https://script.googleusercontent.com/macros/echo?user_content_key=....."> 173.194.222.138:443 (4) [admin@MikroTik] /environment> 

Di awal artikel, saya sudah menulis tentang ini. Saat mengakses URL yang diketahui tetap dari skrip Aplikasi Web, Google mengalihkan ke URL sementara, yang pada gilirannya mengembalikan respons terhadap permintaan. Tapi tidak seperti PHP cURL, mengambil RouterOS tidak tahu bagaimana harus melalui pengalihan, malah mengembalikan kegagalan. Tapi forum.mikrotik.com tidak segera, tetapi ada solusinya. Anda dapat mengarahkan standar mengambil output dari konsol ke file dengan memanggil eksekusi asinkron dalam tugas terpisah dengan membungkus: mengeksekusi. Anda kemudian dapat mengambil URL pengalihan dan mengambil kembali sudah dengan alamat baru. Yang dilakukan di bawah ini.

 #  . Moved Temporarily 302.  fetch  gasfetchout.txt :local jobid [:execute script={/tool fetch url="https://script.google.com/macros/s/A.....A/exec" output=user as-value} file=gasfetchout.txt] #    ,     :while ([:len [/system script job find .id=$jobid ]] > 0) do={ delay 1s } #  gasfetchout.txt,  URL  :local fetchOut [/file get gasfetchout.txt contents] :local startURL [:find $fetchOut "http" -1] :local endURL [:find $fetchOut "\"> " startURL] :local moveURL [:pick $fetchOut $startURL $endURL] :global VPNBookPass2 ([/tool fetch url=$moveURL output=user as-value]->"data") 

Berikut adalah teks lengkap skrip Mikrotik untuk bekerja dengan Aplikasi Web GAS
 # VPNBookScript v4 :local VPNBookpIfName "pptp-out1" :local VPNBookServerAddresses {"PL226.vpnbook.com";"de4.vpnbook.com";"us1.vpnbook.com";"us2.vpnbook.com";"fr1.vpnbook.com ";"fr8.vpnbook.com ";"ca222.vpnbook.com ";"ca198.vpnbook.com"} :local VPNBookErr false :global VPNBookPass :global VPNBookRun :global VPNBookServerIndex :if ([:typeof $VPNBookServerIndex] != "num") do={:set VPNBookServerIndex 0} :if ([/interface pptp-client get $VPNBookpIfName running]) do={ :set VPNBookRun true } else { :if (!$VPNBookRun) do={ :set VPNBookServerIndex ($VPNBookServerIndex + 1) :if ($VPNBookServerIndex>=[:len $VPNBookServerAddresses]) do={:set VPNBookServerIndex 0} } else { :set VPNBookRun false } :if (![/interface pptp-client get $VPNBookpIfName disabled]) do={/interface pptp-client set $VPNBookpIfName disabled=yes} # :do {:set VPNBookPass ([/tool fetch url="http://serv/vpnbookpass_googlescript.php" output=user as-value]->"data")} on-error={:set VPNBookErr true} :do { # First request with Moved Temporarily. Fetch out to gasfetchout.txt :local jobid [:execute script={/tool fetch url="https://script.google.com/macros/s/A.....g/exec" output=user as-value} file=gasfetchout.txt] # Wait end job :while ([:len [/system script job find .id=$jobid ]] > 0) do={ delay 1s } # parse new URL for second fetch :local fetchOut [/file get gasfetchout.txt contents] :local startURL [:find $fetchOut "http" -1] :local endURL [:find $fetchOut "\"> " startURL] :local moveURL [:pick $fetchOut $startURL $endURL] :set VPNBookPass ([/tool fetch url=$moveURL output=user as-value]->"data") } on-error={:set VPNBookErr true} :if (!$VPNBookErr) do={ :if ([/interface pptp-client get $VPNBookpIfName password] != $VPNBookPass) do={/interface pptp-client set $VPNBookpIfName password=$VPNBookPass} :if ([/interface pptp-client get $VPNBookpIfName connect-to] != $VPNBookServerAddresses->$VPNBookServerIndex) do={/interface pptp-client set $VPNBookpIfName connect-to=($VPNBookServerAddresses->$VPNBookServerIndex)} :log info "VPNBook: Attempt to connect to: $($VPNBookServerAddresses->$VPNBookServerIndex). Password: $VPNBookPass" /interface pptp-client set $VPNBookpIfName disabled=no } } 


Bagian 4. Proxy Telegram GAS


Saya memutuskan untuk mengabdikan bagian ini pada iterasi berikutnya untuk mengintegrasikan layanan Telegram ke Mikrotik. Menggunakan GAS di sini adalah murni kepentingan akademis jika bukan karena kenyataan memblokir layanan Telegram, termasuk api.telegram.org, di mana bot bekerja dengan layanan tersebut. Gagasan ini mengulangi gagasan di awal artikel tentang proksi permintaan gambar PNG.
Dalam hal ini, Aplikasi Web GAS ditulis untuk permintaan proxy dari Mikrtotik ke api.telegram.org. Sebagai dasar, saya mengambil skrip yang sudah jadi dari manzoorwanijk, WPTelegram Google Script gist.github.com/manzoorwanijk/ee9ed032caedf2bb0c83dea73bc9a28e . Skrip ini dapat mem-proksi banyak metode API Telegram (tetapi tidak semua). Dalam args, Anda dapat mengirimkan objek JSON yang berisi parameter permintaan, misalnya {"chat_id":"123","text":"HelloWorld"} . Tetapi untuk tugas saya mengirim pesan teks dari RouterOS Mikrtotik, implementasinya tampak rumit dan saya menyederhanakannya. Pada akhirnya, Anda biasanya dapat menulis beberapa skrip Aplikasi Web untuk mem-proksi berbagai metode API Telegram. Inilah implementasi saya untuk metode sendMessage. Lebih lanjut dapat disederhanakan dengan menanamkan nama metode sendMessage yang disebut, dan bahkan bot_token dan chat_id ke dalam tubuh fungsi requestHandler.

 function doGet(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function doPost(e) { if(typeof e !== 'undefined'){ return ContentService.createTextOutput(requestHandler(e)); } } function requestHandler(e){ if (typeof e.parameter.bot_token === 'undefined'){ return 'Error! Bot token not provided'; } else if (typeof e.parameter.method === 'undefined') { return 'Error! Method name not provided'; } else if (typeof e.parameter.chat_id === 'undefined') { return 'Error! Chat id not provide'; } else if (typeof e.parameter.text === 'undefined') { return 'Error! Text not provide'; } /* if(typeof e.parameter.args !== 'undefined'){ var args = e.parameter.args; data.payload = JSON.parse(args); } */ if (e.parameter.method === 'sendMessage') { var data = { "method": "post", "muteHttpExceptions": true, payload : 'chat_id=' + e.parameter.chat_id + '&text=' + e.parameter.text } return UrlFetchApp.fetch('https://api.telegram.org/bot' + e.parameter.bot_token + '/' + e.parameter.method, data).getContentText(); } } 

Setelah menerbitkan skrip di Aplikasi Web, Anda dapat menjalankan permintaan di browser GET untuk memeriksa:

 https://script.google.com/macros/s/A.....A/exec?bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123 

Atau dalam permintaan POST RouterOS:

 :do { /tool fetch url=("https://script.google.com/macros/s/A.....A/exec") keep-result=no http-method=post http-data=("bot_token=3.....3&method=sendMessage&chat_id=2.....3&text=testtext123") } on-error={ } 

Permintaan dibungkus dengan do-on-error, karena, seperti yang ditunjukkan di atas, panggilan pertama untuk mengambil akan mengeluarkan pengecualian "Dipindahkan Sementara 302" dan skrip tanpa penangan kesalahan akan berhenti pada titik ini. Satu panggilan untuk mengambil tanpa meneruskan cukup untuk pesan yang akan dikirim, sehingga panggilan kedua untuk mengambil tidak diperlukan jika Anda tidak perlu objek JSON dikembalikan oleh API Telegram.

Bagian 5. Final


Saya membawa aplikasi saya yang sebenarnya di persimpangan Google Apps Script dengan layanan lain. Anda dapat menghasilkan lebih banyak. Misalnya, tulis bot Telegram di GAS yang akan merespons dengan kata sandi VPNBook dengan permintaan caching untuk mengurangi beban pada VPNBook (Layanan Cache), dan semua ini akan berada dalam satu skrip GAS. Anda dapat menulis pada GAS sistem pencatatan atau konfigurasi cadangan untuk Mikrtotik, yang akan ditempatkan di file Google Docs dan Google Sheets dan banyak lagi.

Source: https://habr.com/ru/post/id475856/


All Articles