Получаем GEO данные средствами Zennoposter

zarufakis

Client
Регистрация
22.03.2019
Сообщения
2 014
Благодарностей
1 435
Баллы
113
Intro

Начиная с версии 7.8.13.0 вместе с Зеннопостером нам дают еще и геобазу в формате MaxMind. Не знаю для кого как, а для меня это новая фишка от ZennoLab прям имба.

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

Если раньше я выполнял дополнительный запрос для получения таймзоны или координат к разным глючным сервисам, которые грешать таймаутами, лимитами и т.д., то сейчас это все кануло в лета.

Может и для кого-то из разработчиков это будет полезно.

Дисклеймер: Спасибо что у нас всех появился новый помощник - ГПТ, используя системпромт пишет код который почти сразу же работает в зенке.

Приступим к реализации:

1.
Для начала я нашел где лежит геобаза, в 99% случаях это: %user%\AppData\Roaming\ZennoLab\ZennoPoster\7\Data\GeoLite2-City.mmdb

2. Сам код копируем в "Общий код":
C#:
namespace Geo
{
    public static class GeoLite2
    {
        private static readonly byte[] Marker = new byte[] { 0xAB, 0xCD, 0xEF, 0x4D, 0x61, 0x78, 0x4D, 0x69, 0x6E, 0x64, 0x2E, 0x63, 0x6F, 0x6D };

        public static string Lookup(string ipAddress, string dbPath = @"C:\Users\user\AppData\Roaming\ZennoLab\ZennoPoster\7\Data\GeoLite2-City.mmdb")
        {
            if (string.IsNullOrEmpty(ipAddress)) return null;

            System.Net.IPAddress parsedIp;
            if (!System.Net.IPAddress.TryParse(ipAddress, out parsedIp)) return null;

            byte[] ipBytes = parsedIp.GetAddressBytes();
            if (ipBytes.Length == 4)
            {
                byte[] tmp = new byte[16];
                tmp[10] = tmp[11] = 0xff;
                Array.Copy(ipBytes, 0, tmp, 12, 4);
                ipBytes = tmp;
            }

            byte[] f = System.IO.File.ReadAllBytes(dbPath);

            int ms = -1;
            for (int i = f.Length - Marker.Length; i >= 0; i--)
            {
                bool ok = true;
                for (int j = 0; j < Marker.Length && ok; j++) ok = f[i + j] == Marker[j];
                if (ok) { ms = i + Marker.Length; break; }
            }
            if (ms < 0) return null;

            int mo = ms;
            var meta = RMap(f, ref mo, ms);
            int nc = Convert.ToInt32(meta["node_count"]);
            int rs = Convert.ToInt32(meta["record_size"]);
            int ds = nc * rs * 2 / 8 + 16;

            int node = 0;
            for (int i = 0; i < 128 && node < nc; i++)
            {
                int bit = (ipBytes[i / 8] >> (7 - (i % 8))) & 1;
                int no = node * rs * 2 / 8;
                int rec = rs == 24 ? (bit == 0 ? (f[no] << 16) | (f[no + 1] << 8) | f[no + 2] : (f[no + 3] << 16) | (f[no + 4] << 8) | f[no + 5])
                        : rs == 28 ? (bit == 0 ? ((f[no + 3] & 0xF0) << 20) | (f[no] << 16) | (f[no + 1] << 8) | f[no + 2] : ((f[no + 3] & 0x0F) << 24) | (f[no + 4] << 16) | (f[no + 5] << 8) | f[no + 6])
                        : (bit == 0 ? (f[no] << 24) | (f[no + 1] << 16) | (f[no + 2] << 8) | f[no + 3] : (f[no + 4] << 24) | (f[no + 5] << 16) | (f[no + 6] << 8) | f[no + 7]);

                if (rec == nc) return null;
                if (rec > nc)
                {
                    int off = (rec - nc) + ds - 16;
                    var d = RVal(f, ref off, ds) as Dictionary<string, object>;
                    if (d == null) return null;
    
                    string cc = GS(d, "country", "iso_code"), cn = GL(d, "country", "names"), cy = GL(d, "city", "names");
                    string lat = GD(d, "location", "latitude"), lon = GD(d, "location", "longitude"), tz = GS(d, "location", "time_zone");
    
                    return "{" +
                        "\"country_code\":\"" + Esc(cc) + "\"," +
                        "\"country_name\":\"" + Esc(cn) + "\"," +
                        "\"city_name\":\"" + Esc(cy) + "\"," +
                        "\"latitude\":\"" + lat + "\"," +
                        "\"longitude\":\"" + lon + "\"," +
                        "\"timezone\":\"" + Esc(tz) + "\"" +
                    "}";
                }
                node = rec;
            }
            return null;
        }

        private static string Esc(string s) { return s == null ? "" : s.Replace("\\", "\\\\").Replace("\"", "\\\""); }

        private static object RVal(byte[] b, ref int p, int dss)
        {
            int c = b[p++], t = c >> 5;
            if (t == 0) t = b[p++] + 7;
            int s = c & 0x1F;
            if (t != 1 && s >= 29) { int n = s - 28; s = 0; for (int i = 0; i < n; i++) s = (s << 8) | b[p++]; s += n == 1 ? 29 : n == 2 ? 285 : 65821; }

            if (t == 1)
            {
                int ps = (c >> 3) & 3, pt = 0;
                if (ps == 0) { pt = ((c & 0x07) << 8) | b[p++]; }
                else if (ps == 1) { pt = ((c & 0x07) << 16) | (b[p++] << 8) | b[p++]; pt += 2048; }
                else if (ps == 2) { pt = ((c & 0x07) << 24) | (b[p++] << 16) | (b[p++] << 8) | b[p++]; pt += 526336; }
                else { pt = (b[p++] << 24) | (b[p++] << 16) | (b[p++] << 8) | b[p++]; }
                int po = dss + pt;
                return RVal(b, ref po, dss);
            }
            if (t == 2) { string r = System.Text.Encoding.UTF8.GetString(b, p, s); p += s; return r; }
            if (t == 3) { byte[] db = new byte[8]; Array.Copy(b, p, db, 0, 8); if (BitConverter.IsLittleEndian) Array.Reverse(db); p += 8; return BitConverter.ToDouble(db, 0); }
            if (t == 5 || t == 6 || t == 8 || t == 9 || t == 10) { long v = 0; for (int i = 0; i < s; i++) v = (v << 8) | b[p++]; return v; }
            if (t == 7) return RMap(b, ref p, dss, s);
            if (t == 11) { object[] a = new object[s]; for (int i = 0; i < s; i++) a[i] = RVal(b, ref p, dss); return a; }
            if (t == 14) return s != 0;
            p += s; return null;
        }

        private static Dictionary<string, object> RMap(byte[] b, ref int p, int dss, int sz = -1)
        {
            if (sz < 0) { int c = b[p++]; sz = c & 0x1F; if (sz >= 29) { int n = sz - 28; sz = 0; for (int i = 0; i < n; i++) sz = (sz << 8) | b[p++]; sz += n == 1 ? 29 : n == 2 ? 285 : 65821; } }
            var m = new Dictionary<string, object>();
            for (int i = 0; i < sz; i++) { string k = RVal(b, ref p, dss) as string; if (k != null) m[k] = RVal(b, ref p, dss); }
            return m;
        }

        private static string GS(Dictionary<string, object> dict, string k1, string k2)
        {
            if (!dict.ContainsKey(k1)) return "";
            var n = dict[k1] as Dictionary<string, object>;
            return n != null && n.ContainsKey(k2) ? Convert.ToString(n[k2]) : "";
        }

        private static string GL(Dictionary<string, object> dict, string k1, string k2)
        {
            if (!dict.ContainsKey(k1)) return "";
            var n = dict[k1] as Dictionary<string, object>;
            if (n == null || !n.ContainsKey(k2)) return "";
            var nm = n[k2] as Dictionary<string, object>;
            return nm != null && nm.ContainsKey("en") ? Convert.ToString(nm["en"]) : "";
        }

        private static string GD(Dictionary<string, object> dict, string k1, string k2)
        {
            if (!dict.ContainsKey(k1)) return "";
            var n = dict[k1] as Dictionary<string, object>;
            if (n == null || !n.ContainsKey(k2)) return "";
            object v = n[k2];
            return v is double ? ((double)v).ToString(System.Globalization.CultureInfo.InvariantCulture) : Convert.ToString(v);
        }
    }
}
3. Вызываем код:
C#:
string json = Geo.GeoLite2.Lookup(project.Variables["IP"].Value);

if (json != null)
{
    project.Variables["geo_json"].Value = json;
    project.Json.FromString(json);

    string country = project.Json.country_name;
    string city = project.Json.city_name;
    string timezone = project.Json.timezone;
    string latitude = project.Json.latitude;
    string longitude = project.Json.longitude;
    string countryCode = project.Json.country_code;

    // project.Variables["latitude"].Value = latitude;
}
3.1. Если база лежит в ином месте:

C#:
string user_name = project.ExecuteMacro("{-Environment.UserName-}");
string json = Geo.GeoLite2.Lookup("8.8.8.8", $@"C:\Users\{user_name}\AppData\Roaming\ZennoLab\ZennoPoster\7\Data\GeoLite2-City.mmdb");
Вызвав наш код мы получаем во вкладке "Json" данные по IP. Так же, можно вернуть дынные в переменные если есть в этом необходимость.

Надеюсь данное решение для вас будет полезным.
Ставьте спасибо и голос за работу

137810
 
Последнее редактирование:

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