Last active
April 13, 2019 00:39
-
-
Save holly-hacker/2e9521532d6196330e52b2a276572db6 to your computer and use it in GitHub Desktop.
Private Google Translate API usage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace HoLLy.DiscordBot.Sandwich.Tools { | |
public static class GoogleTranslate | |
{ | |
private const string TranslateEndpoint = "https://translate.google.com/translate_a/single"; | |
private const string TtsEndpoint = "https://translate.google.com/translate_tts"; | |
public static readonly Dictionary<string, string> Languages = new Dictionary<string, string> { | |
{"auto", "Automatic"}, {"af", "Afrikaans"}, {"sq", "Albanian"}, {"am", "Amharic"}, {"ar", "Arabic"}, {"hy", "Armenian"}, | |
{"az", "Azerbaijani"}, {"eu", "Basque"}, {"be", "Belarusian"}, {"bn", "Bengali"}, {"bs", "Bosnian"}, {"bg", "Bulgarian"}, | |
{"ca", "Catalan"}, {"ceb", "Cebuano"}, {"ny", "Chichewa"}, {"zh-cn", "Chinese Simp."}, {"zh-tw", "Chinese Trad."}, | |
{"co", "Corsican"}, {"hr", "Croatian"}, {"cs", "Czech"}, {"da", "Danish"}, {"nl", "Dutch"}, {"en", "English"}, {"eo", "Esperanto"}, | |
{"et", "Estonian"}, {"tl", "Filipino"}, {"fi", "Finnish"}, {"fr", "French"}, {"fy", "Frisian"}, {"gl", "Galician"}, {"ka", "Georgian"}, | |
{"de", "German"}, {"el", "Greek"}, {"gu", "Gujarati"}, {"ht", "Haitian Creole"}, {"ha", "Hausa"}, {"haw", "Hawaiian"}, {"iw", "Hebrew"}, | |
{"hi", "Hindi"}, {"hmn", "Hmong"}, {"hu", "Hungarian"}, {"is", "Icelandic"}, {"ig", "Igbo"}, {"id", "Indonesian"}, {"ga", "Irish"}, | |
{"it", "Italian"}, {"ja", "Japanese"}, {"jw", "Javanese"}, {"kn", "Kannada"}, {"kk", "Kazakh"}, {"km", "Khmer"}, {"ko", "Korean"}, | |
{"ku", "Kurdish"}, {"ky", "Kyrgyz"}, {"lo", "Lao"}, {"la", "Latin"}, {"lv", "Latvian"}, {"lt", "Lithuanian"}, | |
{"lb", "Luxembourgish"}, {"mk", "Macedonian"}, {"mg", "Malagasy"}, {"ms", "Malay"}, {"ml", "Malayalam"}, {"mt", "Maltese"}, | |
{"mi", "Maori"}, {"mr", "Marathi"}, {"mn", "Mongolian"}, {"my", "Myanmar"}, {"ne", "Nepali"}, {"no", "Norwegian"}, | |
{"ps", "Pashto"}, {"fa", "Persian"}, {"pl", "Polish"}, {"pt", "Portuguese"}, {"ma", "Punjabi"}, {"ro", "Romanian"}, {"ru", "Russian"}, | |
{"sm", "Samoan"}, {"gd", "Scots Gaelic"}, {"sr", "Serbian"}, {"st", "Sesotho"}, {"sn", "Shona"}, {"sd", "Sindhi"}, {"si", "Sinhala"}, | |
{"sk", "Slovak"}, {"sl", "Slovenian"}, {"so", "Somali"}, {"es", "Spanish"}, {"su", "Sundanese"}, {"sw", "Swahili"}, {"sv", "Swedish"}, | |
{"tg", "Tajik"}, {"ta", "Tamil"}, {"te", "Telugu"}, {"th", "Thai"}, {"tr", "Turkish"}, {"uk", "Ukrainian"}, {"ur", "Urdu"}, | |
{"uz", "Uzbek"}, {"vi", "Vietnamese"}, {"cy", "Welsh"}, {"xh", "Xhosa"}, {"yi", "Yiddish"}, {"yo", "Yoruba"}, {"zu", "Zul"}, | |
}; | |
private static string cachedTtk; | |
private static int UnixEpoch => (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds; | |
public static bool SupportsLanguage(string dst) => Languages.ContainsKey(dst); | |
public static async Task<string> Translate(string query, string to = "en", string from = "auto") | |
{ | |
string token = GenerateToken(query, await GetTTK()); | |
string dt = string.Join("&", new [] { "at", "bd", "ex", "ld", "md", "qca", "rw", "rm", "ss", "t" }.Select(x => $"dt={x}")); | |
string queryString = $"?client=webapp&sl={from}&tl={to}&hl={to}&{dt}&pc=1&otf=1&ssel=0&tsel=0&kc=1&tk={token}&q={WebUtility.UrlEncode(query)}"; | |
// adding user-agent somehow fixes unicode inputs. | |
var wc = new WebClient { | |
Headers = { [HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0" } | |
}; | |
string jsonData = await wc.DownloadStringTaskAsync(TranslateEndpoint + queryString); | |
var data = (JArray)JsonConvert.DeserializeObject(jsonData); | |
return string.Join(string.Empty, data[0].Select(x => ((JValue)x[0]).Value?.ToString())); | |
} | |
public static async Task<byte[]> TextToSpeech(string query, string language = "en") | |
{ | |
string token = GenerateToken(query, await GetTTK()); | |
string queryString = $"?ie=UTF-8&q={WebUtility.UrlEncode(query)}&tl={language}&total=1&idx=0&textlen={query.Length-1}&tk={token}&client=webapp"; | |
// adding user-agent somehow fixes unicode inputs. | |
var wc = new WebClient { | |
Headers = { [HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0" } | |
}; | |
return await wc.DownloadDataTaskAsync(TtsEndpoint + queryString); | |
} | |
private static async Task<string> GetTTK() | |
{ | |
if (cachedTtk is null || Convert.ToInt32(cachedTtk.Split('.')[0]) != UnixEpoch / (1000*60*60)) | |
cachedTtk = await DownloadTTK(); | |
return cachedTtk; | |
} | |
private static async Task<string> DownloadTTK() | |
{ | |
var resp = await new WebClient().DownloadStringTaskAsync("https://translate.google.com"); | |
return Regex.Match(resp, @"tkk:\'(\d+\.\d+)\'").Groups[1].Value; | |
} | |
internal static string GenerateToken(string inputString, string tkk) | |
{ | |
string[] tkkSplit = tkk.Split('.'); | |
long tkkFirst = Convert.ToInt64(tkkSplit[0]); | |
var crypted = new List<int>(); | |
for (var i = 0; i < inputString.Length; i++) { | |
int c = inputString[i]; | |
if (c < 0x80) | |
crypted.Add(c); | |
else { | |
if (c < 0x800) { | |
crypted.Add(c >> 6 | 0xC0); | |
} else { | |
if (0xD800 == (c & 0xFC00) && i + 1 < inputString.Length && 0xDC00 == (inputString[i + 1] & 0xFC00)) { | |
c = 0x10000 + ((c & 0x3FF) << 10) + (inputString[++i] & 0x3FF); | |
crypted.Add(c >> 18 | 0xF0); | |
crypted.Add(c >> 12 & 0x3F | 0x80); | |
} else { | |
crypted.Add(c >> 12 | 0xE0); | |
crypted.Add(c >> 6 & 0x3F | 0x80); | |
} | |
} | |
crypted.Add(c & 0x3F | 0x80); | |
} | |
} | |
var param = tkkFirst; | |
foreach (int i in crypted) { | |
param = evalKey(param + i, "+-a^+6"); | |
} | |
param = evalKey(param, "+-3^+b+-f"); | |
param ^= Convert.ToInt64(tkkSplit[1]); | |
uint newParam = (uint)param; | |
newParam %= 1000000; | |
return newParam + "." + (newParam ^ tkkFirst); | |
long evalKey(long input, string key) { | |
Debug.WriteLine("=== " + input); | |
for (int i = 0; i < key.Length - 2; i += 3) { | |
char d1 = key[i + 2]; | |
int d2 = 'a' <= d1 ? d1 - 0x57 : Convert.ToInt32(d1.ToString()); | |
long d = '+' == key[i + 1] ? (int)((uint)input >> d2) : input << d2; | |
input = '+' == key[i] ? (int)(input + d & 0xFFFFFFFF) : input ^ d; | |
} | |
Debug.WriteLine(""); | |
return input; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment