# -*- coding: utf-8 -*- from setuphelpers import * import json import os import shutil from configparser import ConfigParser import requests CONFIG_FILE = "ticketing.ini" CONFIG_TEMPLATE_FILE = "ticketing.ini.example" STATE_FILE = "tickets_state.json" DEFAULT_TIMEOUT = 30 def get_config_path(): return makepath(WAPT.private_dir, CONFIG_FILE) def get_state_path(): return makepath(WAPT.private_dir, STATE_FILE) def install(): config_path = get_config_path() if not isfile(config_path): shutil.copyfile(CONFIG_TEMPLATE_FILE, config_path) def audit(): conf_wapt = load_config() tickets = fetch_odoo_tickets(conf_wapt) previous_tickets_state = load_tickets_state(get_state_path()) tickets_updated = get_updated_ticket_ids(tickets, previous_tickets_state) if tickets_updated: message = "Les tickets suivants ont ete mis a jour depuis la derniere verification : %s" % tickets_updated else: message = "Aucun ticket n'a ete mis a jour depuis la derniere verification." send_to_rocket(message, conf_wapt=conf_wapt) save_tickets_state(get_state_path(), build_tickets_state(tickets)) return "OK" def load_config(): config_path = get_config_path() conf_wapt = ConfigParser() conf_wapt.read(config_path) required_options = [ ("odoo", "url"), ("odoo", "database"), ("odoo", "api_key"), ("rocket", "webhook_url"), ] if not ( conf_wapt.has_option("odoo", "uid") or conf_wapt.has_option("odoo", "username") ): required_options.append(("odoo", "username")) missing_options = [ "%s.%s" % (section, option) for section, option in required_options if not conf_wapt.has_option(section, option) ] if missing_options: raise Exception( "Configuration incomplete dans %s : %s" % (config_path, ", ".join(missing_options)) ) placeholder_options = [ "%s.%s" % (section, option) for section, option in required_options if "CHANGE_ME" in conf_wapt.get(section, option).strip() ] if placeholder_options: raise Exception( "Configuration a renseigner dans %s : %s" % (config_path, ", ".join(placeholder_options)) ) return conf_wapt def get_http_timeout(conf_wapt): return conf_wapt.getint("http", "timeout", fallback=DEFAULT_TIMEOUT) def build_odoo_payload(service, method, args): return { "jsonrpc": "2.0", "method": "call", "params": { "service": service, "method": method, "args": args, }, "id": 1, } def call_odoo_jsonrpc(conf_wapt, service, method, args): odoo_url = conf_wapt.get("odoo", "url").rstrip("/") endpoint = "%s/jsonrpc" % odoo_url response = requests.post( endpoint, json=build_odoo_payload(service, method, args), headers={"Content-Type": "application/json"}, timeout=get_http_timeout(conf_wapt), ) response.raise_for_status() response_data = response.json() if "error" in response_data: raise Exception("Erreur Odoo JSON-RPC : %s" % response_data["error"]) if "result" not in response_data: raise Exception("Reponse Odoo invalide : cle 'result' absente") return response_data["result"] def get_odoo_uid(conf_wapt): if conf_wapt.has_option("odoo", "uid"): return conf_wapt.getint("odoo", "uid") uid = call_odoo_jsonrpc( conf_wapt, "common", "authenticate", [ conf_wapt.get("odoo", "database"), conf_wapt.get("odoo", "username"), conf_wapt.get("odoo", "api_key"), {}, ], ) if not uid: raise Exception("Authentification Odoo impossible") return uid def fetch_odoo_tickets(conf_wapt): uid = get_odoo_uid(conf_wapt) return call_odoo_jsonrpc( conf_wapt, "object", "execute_kw", [ conf_wapt.get("odoo", "database"), uid, conf_wapt.get("odoo", "api_key"), conf_wapt.get("odoo", "model", fallback="helpdesk.ticket"), "search_read", [[]], {"fields": ["id", "write_date"]}, ], ) def load_tickets_state(state_path): if not os.path.exists(state_path): return {} with open(state_path, "r") as state_file: return json.load(state_file) def save_tickets_state(state_path, tickets_state): with open(state_path, "w") as state_file: json.dump(tickets_state, state_file, indent=2, sort_keys=True) def build_tickets_state(tickets): return { str(ticket["id"]): ticket["write_date"] for ticket in tickets } def get_updated_ticket_ids(tickets, previous_tickets_state): tickets_updated = [] for ticket in tickets: ticket_id = str(ticket["id"]) ticket_write_date = ticket["write_date"] if previous_tickets_state.get(ticket_id) != ticket_write_date: tickets_updated.append(ticket["id"]) return tickets_updated def send_to_rocket(message_text, attachments=None, conf_wapt=None): """ Envoie un message a Rocket.Chat via un webhook. :param message_text: Texte du message a envoyer :param attachments: Liste de pieces jointes (facultatif) :param conf_wapt: Configuration deja chargee (facultatif) """ if conf_wapt is None: conf_wapt = load_config() message = { "text": message_text, } if attachments: message["attachments"] = attachments response = requests.post( conf_wapt.get("rocket", "webhook_url"), json=message, headers={"Content-Type": "application/json"}, timeout=get_http_timeout(conf_wapt), ) response.raise_for_status() print("Message envoye avec succes.")