Initial commit: ip2region patch override implementation
This commit is contained in:
commit
89ea22d189
|
|
@ -0,0 +1,29 @@
|
|||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Generated files
|
||||
patches_cache.json
|
||||
*.cache
|
||||
|
||||
# Patch files (users should download their own)
|
||||
patches/*.fix
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
# IP2Region 补丁覆盖工具
|
||||
|
||||
一个轻量级的 Python 工具,用于应用 ip2region 数据库补丁,**无需修改**原始的 xdb 数据库文件。该工具创建一个 JSON 缓存,作为覆盖层,在查询主数据库之前检查补丁。
|
||||
|
||||
## 为什么需要这个工具?
|
||||
|
||||
ip2region 官方项目提供了直接修改 xdb 数据库文件的制作工具(Java/Golang/C++)。但是,有时您可能希望:
|
||||
|
||||
- **无需重建**数据库即可应用补丁
|
||||
- 将补丁与主数据库**分离**
|
||||
- 在应用程序中将补丁用作**覆盖层**
|
||||
- 在提交数据库更改之前**测试**补丁
|
||||
|
||||
此工具提供了一种非破坏性的补丁应用方法。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ 从 `data/fix` 目录解析补丁文件
|
||||
- ✅ 创建 JSON 缓存以支持快速二分查找
|
||||
- ✅ UTF-8 编码支持(Windows 系统自动回退)
|
||||
- ✅ 命令行界面,支持自定义路径
|
||||
- ✅ IP 查找测试功能
|
||||
- ✅ 无需外部依赖(仅使用 Python 标准库)
|
||||
|
||||
## 安装
|
||||
|
||||
### 要求
|
||||
|
||||
- Python 3.7 或更高版本
|
||||
- 无需外部依赖(仅使用标准库)
|
||||
|
||||
### 快速开始
|
||||
|
||||
1. **下载补丁文件**(从 ip2region 仓库):
|
||||
```bash
|
||||
# 创建补丁目录
|
||||
mkdir patches
|
||||
|
||||
# 从以下地址下载补丁文件:
|
||||
# https://github.com/lionsoul2014/ip2region/tree/master/data/fix
|
||||
# 将它们保存为 .fix 文件到 patches/ 目录
|
||||
```
|
||||
|
||||
2. **运行工具**:
|
||||
```bash
|
||||
python patch_override.py
|
||||
```
|
||||
|
||||
3. **在应用程序中使用缓存文件**:
|
||||
```python
|
||||
from patch_override import load_patch_cache, find_patch_for_ip
|
||||
|
||||
cache = load_patch_cache(Path("patches_cache.json"))
|
||||
result = find_patch_for_ip("39.144.0.1", cache)
|
||||
if result:
|
||||
print(f"省份: {result['province']}, 城市: {result['city']}")
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 基本用法
|
||||
|
||||
```bash
|
||||
# 使用默认路径(patches/ 目录,patches_cache.json 输出)
|
||||
python patch_override.py
|
||||
```
|
||||
|
||||
### 自定义路径
|
||||
|
||||
```bash
|
||||
# 指定自定义目录
|
||||
python patch_override.py --patches-dir ./data/fix --output ./cache.json
|
||||
```
|
||||
|
||||
### 测试 IP 查找
|
||||
|
||||
```bash
|
||||
# 使用特定 IP 地址进行测试
|
||||
python patch_override.py --test-ip 39.144.0.1 --test-ip 39.144.10.5
|
||||
```
|
||||
|
||||
### 命令行选项
|
||||
|
||||
```
|
||||
--patches-dir DIR 包含 .fix 补丁文件的目录(默认:patches)
|
||||
--output FILE 输出 JSON 缓存文件路径(默认:patches_cache.json)
|
||||
--test-ip IP 用于验证补丁查找的测试 IP 地址(可重复指定)
|
||||
```
|
||||
|
||||
## 补丁文件格式
|
||||
|
||||
补丁文件应为以下格式:
|
||||
|
||||
```
|
||||
start_ip|end_ip|Country|Province|City|ISP
|
||||
```
|
||||
|
||||
示例:
|
||||
```
|
||||
39.144.0.0|39.144.0.255|中国|山东省|菏泽市|移动
|
||||
39.144.1.0|39.144.9.255|中国|0|0|移动
|
||||
```
|
||||
|
||||
## 集成示例
|
||||
|
||||
以下是如何将此工具集成到您的 IP 地理位置服务中:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from patch_override import load_patch_cache, find_patch_for_ip
|
||||
|
||||
class IPGeolocationService:
|
||||
def __init__(self):
|
||||
self.patch_cache = load_patch_cache(Path("patches_cache.json"))
|
||||
# 初始化您的主 ip2region 数据库...
|
||||
|
||||
def get_location(self, ip: str):
|
||||
# 首先检查补丁(补丁优先)
|
||||
patch_result = find_patch_for_ip(ip, self.patch_cache)
|
||||
if patch_result:
|
||||
return patch_result
|
||||
|
||||
# 回退到主数据库
|
||||
return self._lookup_main_database(ip)
|
||||
```
|
||||
|
||||
## 缓存文件格式
|
||||
|
||||
生成的缓存文件是一个 JSON 文件,结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"patches": [
|
||||
{
|
||||
"start_ip": "39.144.0.0",
|
||||
"end_ip": "39.144.0.255",
|
||||
"start_int": 663748608,
|
||||
"end_int": 663748863,
|
||||
"country": "中国",
|
||||
"province": "山东省",
|
||||
"city": "菏泽市",
|
||||
"isp": "移动",
|
||||
"source": "github-issue-196.fix",
|
||||
"line": 1
|
||||
}
|
||||
],
|
||||
"total_patches": 237,
|
||||
"last_updated": "2024-12-28T03:33:13.643784",
|
||||
"patch_files": [
|
||||
"github-issue-196.fix",
|
||||
"github-issue-200.fix",
|
||||
"github-issue-243.fix"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 与制作工具对比
|
||||
|
||||
| 功能 | 制作工具 | 本工具 |
|
||||
|------|---------|--------|
|
||||
| 修改 xdb 文件 | ✅ 是 | ❌ 否 |
|
||||
| 需要重建 | ✅ 是 | ❌ 否 |
|
||||
| 非破坏性 | ❌ 否 | ✅ 是 |
|
||||
| 覆盖层 | ❌ 否 | ✅ 是 |
|
||||
| 易于更新 | ❌ 否 | ✅ 是 |
|
||||
| Python 支持 | ❌ 否 | ✅ 是 |
|
||||
|
||||
## 更新补丁
|
||||
|
||||
当发布新补丁时:
|
||||
|
||||
1. 将新的 `.fix` 文件下载到您的 `patches/` 目录
|
||||
2. 再次运行工具:`python patch_override.py`
|
||||
3. 缓存将使用所有补丁重新生成
|
||||
4. 重启您的应用程序以加载新缓存
|
||||
|
||||
## 编码支持
|
||||
|
||||
该工具支持多种编码:
|
||||
- UTF-8(主要)
|
||||
- GBK/GB2312(Windows 回退)
|
||||
- UTF-8 with BOM
|
||||
|
||||
中文字符在缓存文件中正确保留。
|
||||
|
||||
## 贡献
|
||||
|
||||
此工具旨在贡献给 ip2region 项目。如果您发现错误或有改进建议:
|
||||
|
||||
1. Fork ip2region 仓库
|
||||
2. 将此工具添加到 `maker/python/` 或 `tools/python/`
|
||||
3. 提交拉取请求
|
||||
|
||||
## 许可证
|
||||
|
||||
此工具遵循与 ip2region 项目相同的许可证(Apache 2.0)。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [ip2region GitHub 仓库](https://github.com/lionsoul2014/ip2region)
|
||||
- [补丁文件位置](https://github.com/lionsoul2014/ip2region/tree/master/data/fix)
|
||||
- [制作工具文档](https://github.com/lionsoul2014/ip2region/tree/master/maker)
|
||||
|
||||
## 作者
|
||||
|
||||
社区贡献 - ip2region 补丁应用的 Python 实现。
|
||||
|
||||
## 致谢
|
||||
|
||||
基于 lionsoul2014 的 ip2region 项目的补丁格式和概念。
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Example usage of the IP2Region Patch Override Tool
|
||||
|
||||
This demonstrates how to integrate the patch override system
|
||||
into your IP geolocation service.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from patch_override import load_patch_cache, find_patch_for_ip
|
||||
|
||||
|
||||
def example_basic_usage():
|
||||
"""Basic example: Load cache and lookup IPs."""
|
||||
print("=" * 60)
|
||||
print("Example: Basic Patch Lookup")
|
||||
print("=" * 60)
|
||||
|
||||
# Load the patch cache
|
||||
cache_file = Path("patches_cache.json")
|
||||
cache = load_patch_cache(cache_file)
|
||||
|
||||
if not cache:
|
||||
print("No cache file found. Run patch_override.py first!")
|
||||
return
|
||||
|
||||
print(f"Loaded {cache.get('total_patches', 0)} patches from cache")
|
||||
print(f"Last updated: {cache.get('last_updated', 'unknown')}")
|
||||
|
||||
# Test IPs
|
||||
test_ips = [
|
||||
"39.144.0.1", # Should match patch
|
||||
"39.144.10.5", # Should match patch
|
||||
"39.144.177.100", # Should match patch
|
||||
"8.8.8.8", # Should not match (no patch)
|
||||
]
|
||||
|
||||
print("\nTesting IP lookups:")
|
||||
for ip in test_ips:
|
||||
result = find_patch_for_ip(ip, cache)
|
||||
if result:
|
||||
print(f" {ip:15} -> {result['province']}, {result['city']} (from patch)")
|
||||
else:
|
||||
print(f" {ip:15} -> No patch found (use main database)")
|
||||
|
||||
|
||||
def example_integration():
|
||||
"""Example: Integration with IP geolocation service."""
|
||||
print("\n" + "=" * 60)
|
||||
print("Example: Service Integration")
|
||||
print("=" * 60)
|
||||
|
||||
class SimpleIPGeolocation:
|
||||
"""Simple example IP geolocation service."""
|
||||
|
||||
def __init__(self, cache_file: Path):
|
||||
"""Initialize with patch cache."""
|
||||
self.patch_cache = load_patch_cache(cache_file)
|
||||
print(f"Service initialized with {self.patch_cache.get('total_patches', 0)} patches")
|
||||
|
||||
def get_location(self, ip: str):
|
||||
"""
|
||||
Get location for an IP address.
|
||||
Checks patches first, then falls back to main database.
|
||||
"""
|
||||
# Check patches first (patches take priority)
|
||||
patch_result = find_patch_for_ip(ip, self.patch_cache)
|
||||
if patch_result:
|
||||
return {
|
||||
**patch_result,
|
||||
'source': 'patch'
|
||||
}
|
||||
|
||||
# In a real implementation, you would query the main database here
|
||||
# For this example, we'll just return None
|
||||
return {
|
||||
'source': 'main_database',
|
||||
'note': 'Would query main ip2region database here'
|
||||
}
|
||||
|
||||
# Initialize service
|
||||
service = SimpleIPGeolocation(Path("patches_cache.json"))
|
||||
|
||||
# Test lookups
|
||||
test_ip = "39.144.0.1"
|
||||
result = service.get_location(test_ip)
|
||||
|
||||
print(f"\nLookup result for {test_ip}:")
|
||||
print(f" Source: {result.get('source')}")
|
||||
if result.get('source') == 'patch':
|
||||
print(f" Province: {result.get('province')}")
|
||||
print(f" City: {result.get('city')}")
|
||||
print(f" Country: {result.get('country')}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("IP2Region Patch Override - Example Usage\n")
|
||||
|
||||
# Run examples
|
||||
example_basic_usage()
|
||||
example_integration()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Examples complete!")
|
||||
print("=" * 60)
|
||||
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
IP2Region Patch Override Tool
|
||||
|
||||
A lightweight Python tool for applying ip2region database patches without
|
||||
modifying the original xdb database files. This creates a JSON cache that
|
||||
acts as an override layer, checking patches before querying the main database.
|
||||
|
||||
Unlike the maker tools (Java/Golang/C++) which modify xdb files directly,
|
||||
this tool provides a non-destructive patch application method.
|
||||
|
||||
Based on patch format from:
|
||||
https://github.com/lionsoul2014/ip2region/tree/master/data/fix
|
||||
|
||||
Usage:
|
||||
python patch_override.py [--patches-dir DIR] [--output FILE]
|
||||
|
||||
Author: Community Contribution
|
||||
License: Apache 2.0 (same as ip2region project)
|
||||
"""
|
||||
|
||||
import json
|
||||
import ipaddress
|
||||
import sys
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
# Configure stdout for UTF-8 on Windows
|
||||
if sys.platform == 'win32':
|
||||
try:
|
||||
if hasattr(sys.stdout, 'reconfigure'):
|
||||
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
else:
|
||||
import io
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def ip_to_int(ip: str) -> int:
|
||||
"""Convert IP address to integer."""
|
||||
try:
|
||||
return int(ipaddress.IPv4Address(ip))
|
||||
except:
|
||||
return 0
|
||||
|
||||
|
||||
def parse_patch_file(patch_path: Path) -> List[Dict]:
|
||||
"""
|
||||
Parse patch file format: start_ip|end_ip|Country|Province|City|ISP
|
||||
|
||||
Returns list of patch entries with IP ranges converted to integers.
|
||||
"""
|
||||
patches = []
|
||||
try:
|
||||
# Try UTF-8 first, fallback to GBK/GB2312 for Windows compatibility
|
||||
encodings = ['utf-8', 'gbk', 'gb2312', 'utf-8-sig']
|
||||
content = None
|
||||
encoding_used = None
|
||||
|
||||
# Read as binary first to avoid any encoding issues
|
||||
with open(patch_path, 'rb') as f:
|
||||
raw_bytes = f.read()
|
||||
|
||||
for enc in encodings:
|
||||
try:
|
||||
content = raw_bytes.decode(enc)
|
||||
encoding_used = enc
|
||||
# Verify Chinese characters decode correctly
|
||||
if '中国' in content[:500] or '北京' in content[:500]:
|
||||
break
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
|
||||
if content is None:
|
||||
print(f"Warning: Could not decode {patch_path} with any encoding")
|
||||
return patches
|
||||
|
||||
# Process lines
|
||||
for line_num, line in enumerate(content.splitlines(), 1):
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
|
||||
# Format: start_ip|end_ip|Country|Province|City|ISP
|
||||
parts = line.split('|')
|
||||
if len(parts) >= 6:
|
||||
start_ip = parts[0].strip()
|
||||
end_ip = parts[1].strip()
|
||||
country = parts[2].strip()
|
||||
province = parts[3].strip()
|
||||
city = parts[4].strip()
|
||||
isp = parts[5].strip()
|
||||
|
||||
# Convert IPs to integers for range checking
|
||||
try:
|
||||
start_int = ip_to_int(start_ip)
|
||||
end_int = ip_to_int(end_ip)
|
||||
|
||||
if start_int > 0 and end_int > 0:
|
||||
patches.append({
|
||||
'start_ip': start_ip,
|
||||
'end_ip': end_ip,
|
||||
'start_int': start_int,
|
||||
'end_int': end_int,
|
||||
'country': country,
|
||||
'province': province,
|
||||
'city': city,
|
||||
'isp': isp,
|
||||
'source': patch_path.name,
|
||||
'line': line_num
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"Warning: Invalid IP range in {patch_path.name} line {line_num}: {e}")
|
||||
continue
|
||||
|
||||
if encoding_used and encoding_used != 'utf-8':
|
||||
print(f" Note: File read with {encoding_used} encoding")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error parsing {patch_path}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
return patches
|
||||
|
||||
|
||||
def build_patch_cache(patches_dir: Path, output_file: Path) -> Dict:
|
||||
"""
|
||||
Build a cache of all patches for fast lookup.
|
||||
|
||||
Args:
|
||||
patches_dir: Directory containing .fix patch files
|
||||
output_file: Path to save the JSON cache file
|
||||
|
||||
Returns:
|
||||
Dictionary with patch cache data
|
||||
"""
|
||||
if not patches_dir.exists():
|
||||
print(f"Patch directory not found: {patches_dir}")
|
||||
return {}
|
||||
|
||||
# Find all patch files
|
||||
patch_files = list(patches_dir.glob("*.fix"))
|
||||
if not patch_files:
|
||||
print(f"No patch files found in {patches_dir}")
|
||||
return {}
|
||||
|
||||
print(f"Found {len(patch_files)} patch file(s):")
|
||||
for pf in patch_files:
|
||||
print(f" - {pf.name}")
|
||||
|
||||
# Parse all patches
|
||||
all_patches = []
|
||||
for patch_file in patch_files:
|
||||
patches = parse_patch_file(patch_file)
|
||||
print(f" Parsed {len(patches)} entries from {patch_file.name}")
|
||||
all_patches.extend(patches)
|
||||
|
||||
print(f"\nTotal patches: {len(all_patches)}")
|
||||
|
||||
if not all_patches:
|
||||
return {}
|
||||
|
||||
# Build cache structure: list of patches sorted by start_int for binary search
|
||||
patches_sorted = sorted(all_patches, key=lambda x: x['start_int'])
|
||||
|
||||
# Save to cache file with proper UTF-8 encoding
|
||||
cache_data = {
|
||||
'patches': patches_sorted,
|
||||
'total_patches': len(patches_sorted),
|
||||
'last_updated': datetime.now().isoformat(),
|
||||
'patch_files': [pf.name for pf in patch_files]
|
||||
}
|
||||
|
||||
# Ensure output directory exists
|
||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Save with UTF-8 encoding
|
||||
with open(output_file, 'w', encoding='utf-8', newline='\n') as f:
|
||||
json.dump(cache_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"\nPatch cache saved to: {output_file}")
|
||||
print(f"Total patches cached: {len(patches_sorted)}")
|
||||
|
||||
return cache_data
|
||||
|
||||
|
||||
def load_patch_cache(cache_file: Path) -> Dict:
|
||||
"""Load patch cache from file."""
|
||||
if not cache_file.exists():
|
||||
return {}
|
||||
|
||||
try:
|
||||
# Try UTF-8 first, with fallback encodings
|
||||
encodings = ['utf-8', 'utf-8-sig', 'gbk', 'gb2312']
|
||||
cache_data = None
|
||||
|
||||
for enc in encodings:
|
||||
try:
|
||||
with open(cache_file, 'r', encoding=enc) as f:
|
||||
cache_data = json.load(f)
|
||||
break
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
|
||||
if cache_data is None:
|
||||
print(f"Warning: Could not decode cache file with any encoding")
|
||||
return {}
|
||||
|
||||
return cache_data
|
||||
except Exception as e:
|
||||
print(f"Error loading patch cache: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
def find_patch_for_ip(ip: str, cache: Dict) -> Optional[Dict]:
|
||||
"""
|
||||
Find patch entry for a given IP address using binary search.
|
||||
|
||||
Args:
|
||||
ip: IP address string
|
||||
cache: Patch cache dictionary
|
||||
|
||||
Returns:
|
||||
Patch data dict if IP falls within any patch range, None otherwise
|
||||
"""
|
||||
patches = cache.get('patches', [])
|
||||
if not patches:
|
||||
return None
|
||||
|
||||
try:
|
||||
ip_int = ip_to_int(ip)
|
||||
if ip_int == 0:
|
||||
return None
|
||||
|
||||
# Binary search for matching range
|
||||
left, right = 0, len(patches) - 1
|
||||
|
||||
while left <= right:
|
||||
mid = (left + right) // 2
|
||||
patch = patches[mid]
|
||||
|
||||
start_int = patch.get('start_int', 0)
|
||||
end_int = patch.get('end_int', 0)
|
||||
|
||||
if start_int <= ip_int <= end_int:
|
||||
# Found matching range - return location data
|
||||
return {
|
||||
'province': patch.get('province', ''),
|
||||
'city': patch.get('city', ''),
|
||||
'country': patch.get('country', '中国'),
|
||||
'isp': patch.get('isp', '')
|
||||
}
|
||||
elif ip_int < start_int:
|
||||
right = mid - 1
|
||||
else:
|
||||
left = mid + 1
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error finding patch for IP {ip}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to build and test patch cache."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='IP2Region Patch Override Tool - Create JSON cache from patch files',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Use default paths (patches/ and patches_cache.json)
|
||||
python patch_override.py
|
||||
|
||||
# Specify custom directories
|
||||
python patch_override.py --patches-dir ./data/fix --output ./cache.json
|
||||
|
||||
# Test with specific IPs
|
||||
python patch_override.py --test-ip 39.144.0.1 --test-ip 39.144.10.5
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--patches-dir',
|
||||
type=str,
|
||||
default='patches',
|
||||
help='Directory containing .fix patch files (default: patches)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
type=str,
|
||||
default='patches_cache.json',
|
||||
help='Output JSON cache file path (default: patches_cache.json)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--test-ip',
|
||||
action='append',
|
||||
dest='test_ips',
|
||||
help='Test IP addresses to verify patch lookup (can be specified multiple times)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
patches_dir = Path(args.patches_dir)
|
||||
output_file = Path(args.output)
|
||||
|
||||
print("=" * 60)
|
||||
print("IP2Region Patch Override Tool")
|
||||
print("=" * 60)
|
||||
|
||||
# Build patch cache
|
||||
print(f"\n[1/2] Building patch cache...")
|
||||
print(f" Patches directory: {patches_dir}")
|
||||
print(f" Output file: {output_file}")
|
||||
|
||||
cache = build_patch_cache(patches_dir, output_file)
|
||||
|
||||
if not cache:
|
||||
print("\nNo patches found. Exiting.")
|
||||
return
|
||||
|
||||
# Test patch lookup if IPs provided
|
||||
if args.test_ips:
|
||||
print(f"\n[2/2] Testing patch lookup...")
|
||||
for test_ip in args.test_ips:
|
||||
patch = find_patch_for_ip(test_ip, cache)
|
||||
if patch:
|
||||
province = patch.get('province', '')
|
||||
city = patch.get('city', '')
|
||||
print(f" {test_ip} -> {province}, {city} (from patch)")
|
||||
else:
|
||||
print(f" {test_ip} -> No patch found (will use main database)")
|
||||
else:
|
||||
# Default test IPs
|
||||
print(f"\n[2/2] Testing patch lookup...")
|
||||
test_ips = ['39.144.0.1', '39.144.10.5', '39.144.177.100']
|
||||
for test_ip in test_ips:
|
||||
patch = find_patch_for_ip(test_ip, cache)
|
||||
if patch:
|
||||
province = patch.get('province', '')
|
||||
city = patch.get('city', '')
|
||||
print(f" {test_ip} -> {province}, {city} (from patch)")
|
||||
else:
|
||||
print(f" {test_ip} -> No patch found (will use main database)")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Patch cache creation complete!")
|
||||
print("\nNext steps:")
|
||||
print("1. Use the cache file in your IP geolocation service")
|
||||
print("2. Check patches before querying the main xdb database")
|
||||
print("3. Patches take priority over database results")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# IP2Region Patch Override Tool - Requirements
|
||||
#
|
||||
# This tool uses only Python standard library modules.
|
||||
# No external dependencies are required!
|
||||
#
|
||||
# Required Python version: 3.7 or higher
|
||||
#
|
||||
# Standard library modules used:
|
||||
# - json (JSON serialization)
|
||||
# - ipaddress (IP address handling)
|
||||
# - sys (system-specific parameters)
|
||||
# - argparse (command-line argument parsing)
|
||||
# - pathlib (path handling)
|
||||
# - typing (type hints)
|
||||
# - datetime (timestamp generation)
|
||||
#
|
||||
# All these modules are included in Python 3.7+ standard library.
|
||||
#
|
||||
# To verify your Python version:
|
||||
# python --version
|
||||
#
|
||||
# Should show Python 3.7.0 or higher.
|
||||
|
||||
Loading…
Reference in New Issue