178 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import time
 | 
						|
import feedparser
 | 
						|
import requests
 | 
						|
import sqlite3
 | 
						|
import logging
 | 
						|
import os
 | 
						|
import requests
 | 
						|
from bs4 import BeautifulSoup
 | 
						|
import socket
 | 
						|
import sys
 | 
						|
 | 
						|
def join_lines_with_newline(lines):
 | 
						|
    return "\n".join(lines)
 | 
						|
 | 
						|
def get_ip_address(domain, retries=10, delay=1):
 | 
						|
    """
 | 
						|
    Resolves the IP address of a domain, retrying up to `retries` times if it fails.
 | 
						|
    
 | 
						|
    Args:
 | 
						|
        domain (str): The domain name to resolve.
 | 
						|
        retries (int): Number of retry attempts (default is 10).
 | 
						|
        delay (int): Delay between retries in seconds (default is 1 second).
 | 
						|
    
 | 
						|
    Returns:
 | 
						|
        str: The IP address if resolved successfully.
 | 
						|
    
 | 
						|
    Raises:
 | 
						|
        RuntimeError: If unable to resolve the domain after all attempts.
 | 
						|
    """
 | 
						|
    for attempt in range(1, retries + 1):
 | 
						|
        try:
 | 
						|
            ip_address = socket.gethostbyname(domain)
 | 
						|
            logging.info(f"Successfully resolved {domain} to {ip_address}")
 | 
						|
            return ip_address
 | 
						|
        except socket.gaierror:
 | 
						|
            logging.warning(f"Attempt {attempt} failed. Retrying...")
 | 
						|
            time.sleep(delay)
 | 
						|
    
 | 
						|
    raise RuntimeError(f"Unable to resolve domain '{domain}' after {retries} attempts.")
 | 
						|
 | 
						|
def extract_changelog_top(koji_link, host_header):
 | 
						|
    try:
 | 
						|
        response = requests.get(koji_link, headers={'Host': host_header})
 | 
						|
    except Exception as e:
 | 
						|
        logging.error(f"Koji link failed: {str(e)}")
 | 
						|
        return []
 | 
						|
        
 | 
						|
    soup = BeautifulSoup(response.text, 'html.parser')
 | 
						|
    changelog_td = soup.find('td', class_='changelog')
 | 
						|
 | 
						|
    if changelog_td:
 | 
						|
        changelog_text = changelog_td.get_text(strip=False)
 | 
						|
        lines = changelog_text.split('\n')
 | 
						|
        
 | 
						|
        result = []
 | 
						|
        seen = set()
 | 
						|
        for line in lines:
 | 
						|
            stripped_line = line.strip()
 | 
						|
            if not stripped_line:
 | 
						|
                if result:
 | 
						|
                    break
 | 
						|
                continue
 | 
						|
            if stripped_line not in seen:
 | 
						|
                seen.add(stripped_line)
 | 
						|
                if not result or stripped_line.startswith('-'):
 | 
						|
                    result.append(stripped_line)
 | 
						|
        
 | 
						|
        return join_lines_with_newline(result)
 | 
						|
    else:
 | 
						|
        return []
 | 
						|
 | 
						|
# Updated Rocket.Chat webhook URL
 | 
						|
ROCKET_CHAT_URL = "https://chat.koozali.org/hooks/66d24441effca216c2ca309f/KJLaNwc5vyHwqz5MhRDpBkKWnQuAfsCX3xZMHxPhpuqmFgBn"
 | 
						|
 | 
						|
# Set up logging
 | 
						|
log_file = "/var/log/Koji2Rocket.log"
 | 
						|
logging.basicConfig(filename=log_file, level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 | 
						|
os.makedirs(os.path.dirname(log_file), exist_ok=True)
 | 
						|
 | 
						|
# Database setup
 | 
						|
def setup_database():
 | 
						|
    conn = sqlite3.connect('sent_builds.db')
 | 
						|
    cursor = conn.cursor()
 | 
						|
    cursor.execute('''
 | 
						|
        CREATE TABLE IF NOT EXISTS sent_builds (
 | 
						|
            id TEXT PRIMARY KEY
 | 
						|
        )
 | 
						|
    ''')
 | 
						|
    conn.commit()
 | 
						|
    conn.close()
 | 
						|
 | 
						|
def has_build_been_sent(build_id):
 | 
						|
    conn = sqlite3.connect('sent_builds.db')
 | 
						|
    cursor = conn.cursor()
 | 
						|
    cursor.execute('SELECT * FROM sent_builds WHERE id = ?', (build_id,))
 | 
						|
    exists = cursor.fetchone() is not None
 | 
						|
    conn.close()
 | 
						|
    return exists
 | 
						|
 | 
						|
def mark_build_as_sent(build_id):
 | 
						|
    conn = sqlite3.connect('sent_builds.db')
 | 
						|
    cursor = conn.cursor()
 | 
						|
    cursor.execute('INSERT OR IGNORE INTO sent_builds (id) VALUES (?)', (build_id,))
 | 
						|
    conn.commit()
 | 
						|
    conn.close()
 | 
						|
 | 
						|
def send_to_rocket_chat(build_title, build_link, build_id, changelog):
 | 
						|
    payload = {
 | 
						|
        "alias": "Koji",
 | 
						|
        "text": f"Build Notification - ID: {build_id}\n{changelog}",
 | 
						|
        "attachments": [
 | 
						|
            {
 | 
						|
                "title": build_title,
 | 
						|
                "title_link": build_link,
 | 
						|
                "color": "#764FA5"
 | 
						|
            }
 | 
						|
        ]
 | 
						|
    }
 | 
						|
    response = requests.post(ROCKET_CHAT_URL, json=payload)
 | 
						|
    if response.status_code == 200:
 | 
						|
        logging.info(f"Build notification sent successfully: {build_title} (ID: {build_id})")
 | 
						|
    else:
 | 
						|
        logging.error(f"Failed to send build notification: {response.status_code} - {response.text}")
 | 
						|
 | 
						|
def send_startup_message():
 | 
						|
    payload = {
 | 
						|
        "alias": "Koji",
 | 
						|
        "text": "Koji to Rocket.Chat integration started successfully.",
 | 
						|
    }
 | 
						|
    response = requests.post(ROCKET_CHAT_URL, json=payload)
 | 
						|
    if response.status_code == 200:
 | 
						|
        logging.info("Startup message sent successfully to Rocket.Chat.")
 | 
						|
    else:
 | 
						|
        logging.error(f"Failed to send startup message: {response.status_code} - {response.text}")
 | 
						|
 | 
						|
def fetch_koji_feed(koji_rss_url, host_header):
 | 
						|
    try:
 | 
						|
        response = requests.get(koji_rss_url, headers={'Host': host_header})
 | 
						|
        feed = feedparser.parse(response.content)
 | 
						|
        return feed.entries
 | 
						|
    except Exception as e:
 | 
						|
        logging.error(f"RSS request to koji failed: {str(e)}")
 | 
						|
        return []
 | 
						|
 | 
						|
def main():
 | 
						|
    setup_database()
 | 
						|
    send_startup_message()
 | 
						|
 | 
						|
    original_domain = "koji.koozali.org"
 | 
						|
    try:
 | 
						|
        koji_ip = get_ip_address(original_domain)
 | 
						|
    except RuntimeError as e:
 | 
						|
        logging.error(str(e))
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
    # Create IP-based URL and preserve original domain for headers
 | 
						|
    koji_rss_url = f"http://{koji_ip}/koji/recentbuilds?feed=rss"
 | 
						|
    logging.info(f"Using Koji RSS URL: {koji_rss_url}")
 | 
						|
 | 
						|
    while True:
 | 
						|
        entries = fetch_koji_feed(koji_rss_url, original_domain)
 | 
						|
        
 | 
						|
        for entry in entries:
 | 
						|
            build_title = entry.title.strip()
 | 
						|
            original_link = entry.link
 | 
						|
            build_link = original_link.replace(original_domain, koji_ip)
 | 
						|
            build_id = build_title.split(':')[1].split(',')[0].strip()
 | 
						|
            changelog = extract_changelog_top(build_link, original_domain)
 | 
						|
 | 
						|
            if not has_build_been_sent(build_id):
 | 
						|
                send_to_rocket_chat(build_title, build_link, build_id, changelog)
 | 
						|
                mark_build_as_sent(build_id)
 | 
						|
        
 | 
						|
        time.sleep(60)
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    main()
 |