Skip to content

Instantly share code, notes, and snippets.

@holly-hacker
Last active April 13, 2019 00:39
Show Gist options
  • Select an option

  • Save holly-hacker/2e9521532d6196330e52b2a276572db6 to your computer and use it in GitHub Desktop.

Select an option

Save holly-hacker/2e9521532d6196330e52b2a276572db6 to your computer and use it in GitHub Desktop.

Revisions

  1. holly-hacker revised this gist Apr 13, 2019. 1 changed file with 57 additions and 15 deletions.
    72 changes: 57 additions & 15 deletions Translator.cs
    Original file line number Diff line number Diff line change
    @@ -1,26 +1,68 @@
    namespace HoLLy.DiscordBot.Sandwich.Tools {
    public static class Translator
    public static class GoogleTranslate
    {
    private const string ApiEndpoint = "https://translate.google.com/translate_a/single";
    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")
    {
    if (cachedTtk is null || Convert.ToInt32(cachedTtk.Split('.')[0]) != UnixEpoch / (1000*60*60))
    cachedTtk = await DownloadTTK();

    string token = GenerateToken(query, cachedTtk);
    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}&th={to}&hl={to}&{dt}&otf=2&ssel=0&tsel=0&kc=2&tk={token}&q={query}";
    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)}";

    string jsonData = await new WebClient().DownloadStringTaskAsync(ApiEndpoint + queryString);
    // 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);
    var y = (JValue)data[0][0][0];
    return (string)y.Value;
    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()
    @@ -32,7 +74,7 @@ private static async Task<string> DownloadTTK()
    internal static string GenerateToken(string inputString, string tkk)
    {
    string[] tkkSplit = tkk.Split('.');
    int tkkFirst = Convert.ToInt32(tkkSplit[0]);
    long tkkFirst = Convert.ToInt64(tkkSplit[0]);
    var crypted = new List<int>();
    for (var i = 0; i < inputString.Length; i++) {
    int c = inputString[i];
    @@ -61,17 +103,17 @@ internal static string GenerateToken(string inputString, string tkk)
    param = evalKey(param + i, "+-a^+6");
    }
    param = evalKey(param, "+-3^+b+-f");
    param ^= Convert.ToInt32(tkkSplit[1]);
    param ^= Convert.ToInt64(tkkSplit[1]);
    uint newParam = (uint)param;
    newParam %= 1000000;
    return newParam + "." + (newParam ^ tkkFirst);

    int evalKey(int input, string key) {
    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 d = 'a' <= d1 ? d1 - 0x57 : Convert.ToInt32(d1.ToString());
    d = '+' == key[i + 1] ? (int)((uint)input >> d) : input << d;
    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;
    }
  2. holly-hacker created this gist Apr 8, 2019.
    83 changes: 83 additions & 0 deletions Translator.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,83 @@
    namespace HoLLy.DiscordBot.Sandwich.Tools {
    public static class Translator
    {
    private const string ApiEndpoint = "https://translate.google.com/translate_a/single";

    private static string cachedTtk;

    private static int UnixEpoch => (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;

    public static async Task<string> Translate(string query, string to = "en", string from = "auto")
    {
    if (cachedTtk is null || Convert.ToInt32(cachedTtk.Split('.')[0]) != UnixEpoch / (1000*60*60))
    cachedTtk = await DownloadTTK();

    string token = GenerateToken(query, cachedTtk);

    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}&th={to}&hl={to}&{dt}&otf=2&ssel=0&tsel=0&kc=2&tk={token}&q={query}";

    string jsonData = await new WebClient().DownloadStringTaskAsync(ApiEndpoint + queryString);
    var data = (JArray)JsonConvert.DeserializeObject(jsonData);
    var y = (JValue)data[0][0][0];
    return (string)y.Value;
    }

    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('.');
    int tkkFirst = Convert.ToInt32(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.ToInt32(tkkSplit[1]);
    uint newParam = (uint)param;
    newParam %= 1000000;
    return newParam + "." + (newParam ^ tkkFirst);

    int evalKey(int input, string key) {
    Debug.WriteLine("=== " + input);
    for (int i = 0; i < key.Length - 2; i += 3) {
    char d1 = key[i + 2];
    int d = 'a' <= d1 ? d1 - 0x57 : Convert.ToInt32(d1.ToString());
    d = '+' == key[i + 1] ? (int)((uint)input >> d) : input << d;

    input = '+' == key[i] ? (int)(input + d & 0xFFFFFFFF) : input ^ d;
    }
    Debug.WriteLine("");
    return input;
    }
    }
    }
    }