diff --git a/chatgpt_bot/__init__.py b/chatgpt_bot/__init__.py new file mode 100644 index 0000000..19240f4 --- /dev/null +++ b/chatgpt_bot/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models \ No newline at end of file diff --git a/chatgpt_bot/__manifest__.py b/chatgpt_bot/__manifest__.py new file mode 100644 index 0000000..ebd16d2 --- /dev/null +++ b/chatgpt_bot/__manifest__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +{ + 'name': "OdooBot ChatGPT AI integration", + + 'summary': """ + This module integrates the response from ChatGPT into Odoo's built-in chatbot, OdooBot. + """, + + 'description': """ + This module allows users to leverage the advanced natural language processing capabilities + of ChatGPT within Odoo's user-friendly interface. By integrating ChatGPT's responses into OdooBot, + users can easily access the powerful language model's insights and capabilities without having to + navigate away from the Odoo platform. This integration can be used to enhance the functionality of + OdooBot, providing more accurate and detailed responses to user queries and improving overall user experience. + """, + + 'author': "FL1 sro", + 'website': "https://fl1.cz", + "images": ["static/description/banner.png", "static/description/gif_chat.gif"], + # Categories can be used to filter modules in modules listing + # Check https://github.com/odoo/odoo/blob/18.0/odoo/addons/base/data/ir_module_category_data.xml + # for the full list + 'category': 'Odex25-base', + 'version': '18.0.0.1', + 'license': 'AGPL-3', + + # any module necessary for this one to work correctly + 'depends': ['base', 'mail', 'queue_job'], +'installable': True, + 'application': True, # <-- Important if you want it to appear in "Apps" + 'auto_install': False, + # always loaded + 'data': [ + # 'security/ir.model.access.csv', + 'views/res_config_settings.xml', + + ], + # only loaded in demonstration mode + 'demo': [ + 'demo/demo.xml', + ], + "external_dependencies": { + "python": ["openai"] + }, + 'price': 0.00, + 'currency': 'EUR', + +} \ No newline at end of file diff --git a/chatgpt_bot/controllers/__init__.py b/chatgpt_bot/controllers/__init__.py new file mode 100644 index 0000000..15c7016 --- /dev/null +++ b/chatgpt_bot/controllers/__init__.py @@ -0,0 +1,2 @@ + +from . import controllers \ No newline at end of file diff --git a/chatgpt_bot/controllers/controllers.py b/chatgpt_bot/controllers/controllers.py new file mode 100644 index 0000000..e69de29 diff --git a/chatgpt_bot/demo/demo.xml b/chatgpt_bot/demo/demo.xml new file mode 100644 index 0000000..17ad66e --- /dev/null +++ b/chatgpt_bot/demo/demo.xml @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/chatgpt_bot/images/gif_chat.gif b/chatgpt_bot/images/gif_chat.gif new file mode 100644 index 0000000..2403faf Binary files /dev/null and b/chatgpt_bot/images/gif_chat.gif differ diff --git a/chatgpt_bot/images/image1.png b/chatgpt_bot/images/image1.png new file mode 100644 index 0000000..da579e6 Binary files /dev/null and b/chatgpt_bot/images/image1.png differ diff --git a/chatgpt_bot/images/image2.png b/chatgpt_bot/images/image2.png new file mode 100644 index 0000000..bcca92e Binary files /dev/null and b/chatgpt_bot/images/image2.png differ diff --git a/chatgpt_bot/images/image3.png b/chatgpt_bot/images/image3.png new file mode 100644 index 0000000..953294d Binary files /dev/null and b/chatgpt_bot/images/image3.png differ diff --git a/chatgpt_bot/images/image4.png b/chatgpt_bot/images/image4.png new file mode 100644 index 0000000..7ab9256 Binary files /dev/null and b/chatgpt_bot/images/image4.png differ diff --git a/chatgpt_bot/models/__init__.py b/chatgpt_bot/models/__init__.py new file mode 100644 index 0000000..2c78fad --- /dev/null +++ b/chatgpt_bot/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import res_config_setting +from . import mail_bot +from . import res_users \ No newline at end of file diff --git a/chatgpt_bot/models/__pycache__/__init__.cpython-311.pyc b/chatgpt_bot/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000..2a95cc7 Binary files /dev/null and b/chatgpt_bot/models/__pycache__/__init__.cpython-311.pyc differ diff --git a/chatgpt_bot/models/__pycache__/mail_bot.cpython-311.pyc b/chatgpt_bot/models/__pycache__/mail_bot.cpython-311.pyc new file mode 100644 index 0000000..b1b84c2 Binary files /dev/null and b/chatgpt_bot/models/__pycache__/mail_bot.cpython-311.pyc differ diff --git a/chatgpt_bot/models/__pycache__/res_config_setting.cpython-311.pyc b/chatgpt_bot/models/__pycache__/res_config_setting.cpython-311.pyc new file mode 100644 index 0000000..cc133ca Binary files /dev/null and b/chatgpt_bot/models/__pycache__/res_config_setting.cpython-311.pyc differ diff --git a/chatgpt_bot/models/__pycache__/res_users.cpython-311.pyc b/chatgpt_bot/models/__pycache__/res_users.cpython-311.pyc new file mode 100644 index 0000000..fe1924f Binary files /dev/null and b/chatgpt_bot/models/__pycache__/res_users.cpython-311.pyc differ diff --git a/chatgpt_bot/models/mail_bot.py b/chatgpt_bot/models/mail_bot.py new file mode 100644 index 0000000..7c9279b --- /dev/null +++ b/chatgpt_bot/models/mail_bot.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +from odoo import api, models, _ +from odoo.exceptions import UserError, ValidationError +from bs4 import BeautifulSoup as BS + +# Note: Use the modern OpenAI client +# pip install openai +try: + from openai import OpenAI +except Exception: # pragma: no cover + OpenAI = None + + +class ChatGptBot(models.AbstractModel): + _inherit = 'mail.bot' + _description = 'ChatGPT OdooBot' + + ################################################################################################# + # ORM FUNCTIONS # + ################################################################################################# + + def create_content(self): + """ + Generates HTML content and SEO JSON via OpenAI and writes back to fields if present. + Expected optional fields on the record: + - text_chatgpt (input prompt) + - content (HTML output) + - website_meta_title / website_meta_description / website_meta_keywords (SEO) + - is_publish_now / is_published / is_elaborate + This method is defensive: if fields do not exist on the record, it skips setting them. + """ + # Ensure running on a single record or abstractly use 'self' as context holder + record = self + + # Validate input field existence + text = getattr(record, 'text_chatgpt', None) + if not text: + raise UserError(_("Please provide a prompt (text_chatgpt) before generating content.")) + + api_key = self.env['ir.config_parameter'].sudo().get_param('chatgpt_api_key') + if not api_key: + raise UserError(_("No OpenAI API key configured. Set it in Settings → ChatGPT OdooBot.")) + + if OpenAI is None: + raise UserError(_("The OpenAI Python package is not installed. Please install 'openai'.")) + + # Define an example JSON schema for SEO guidance + ex_json = { + "title": "Sample Title", + "description": "Short description (max 160 characters)", + "keywords": "keyword1, keyword2, keyword3" + } + + client = OpenAI(api_key=api_key) + + # Prompt instructs the model to output: 1) an HTML
...
, then 2) a pure JSON block + system_msg = "You are a helpful assistant that writes clean HTML and valid JSON." + user_msg = ( + f"{text}\n\n" + "Output requirements:\n" + "1) First, return an HTML block wrapped strictly between
and
with headings, paragraphs, and links.\n" + "2) Immediately after the closing , return ONLY a valid JSON object (no backticks, no labels)\n" + f"matching this schema and rules (description max 160 chars): {ex_json}\n" + ) + + try: + resp = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": system_msg}, + {"role": "user", "content": user_msg}, + ], + max_tokens=1200, + temperature=0.7, + ) + full = resp.choices[0].message.content or "" + except Exception as e: + raise UserError(_("OpenAI request failed: %s") % str(e)) + + # Split HTML and JSON by the first + html_part, json_part = "", "" + if "" in full: + html_part = full.split("", 1)[0] + "" + json_part = full.split("", 1)[1].strip() + + # Parse SEO JSON if present + title = descr = kw = None + if json_part: + import json + try: + seo = json.loads(json_part) + title = seo.get("title") + descr = seo.get("description") + kw = seo.get("keywords") + except Exception: + # We don't hard-fail if JSON is malformed; raise a friendly error + raise ValidationError(_("The SEO JSON response is not valid JSON.")) + + # Assign fields defensively if they exist on the record + def safe_set(rec, field, value): + if hasattr(rec, field): + try: + setattr(rec, field, value) + except Exception: + pass + + safe_set(record, 'content', html_part or "") + if title: + safe_set(record, 'website_meta_title', title) + if descr: + safe_set(record, 'website_meta_description', descr) + if kw: + safe_set(record, 'website_meta_keywords', kw) + + # Handle publish flags if the model uses them + is_publish_now = getattr(record, 'is_publish_now', False) + if is_publish_now and hasattr(record, 'is_published'): + safe_set(record, 'is_published', True) + elif hasattr(record, 'is_published'): + safe_set(record, 'is_published', False) + + safe_set(record, 'is_elaborate', True) + + return True + + ################################################################################################# + # CUSTOM FUNCTIONS # + ################################################################################################# + + def _get_answer(self, record, body, values, command=None): + """ + Odoo 18-compatible signature. Adds #enable / #disable and routes to ChatGPT if enabled. + """ + res = super()._get_answer(record, body, values, command=command) + + # Simple toggles + if body.strip().lower() == "#enable": + self.env.user.odoobot_state = 'chatgpt' + return _("ChatGPT enabled") + if body.strip().lower() == "#disable": + self.env.user.odoobot_state = 'disabled' + return _("ChatGPT disabled") + + # Build a short context from last messages (plaintext only) + channel = self.env['mail.channel'].browse(record.id) + last_ids = channel.message_ids.ids + messages = self.env['mail.message'].search([('id', 'in', last_ids)], order='id desc', limit=2).mapped('body') + old_conv = "" + for msg in messages: + if msg: + old_conv += BS(msg, 'html.parser').get_text() + "\n" + + # Route to ChatGPT if enabled + if self.env.user.odoobot_state == 'chatgpt': + return self._chatgpt_reply(record, body, old_conv) + return res + + def _chatgpt_reply(self, record, body, context_text=""): + api_key = self.env['ir.config_parameter'].sudo().get_param('chatgpt_api_key') + if not api_key: + raise UserError(_("No OpenAI API key configured. Set it in Settings → ChatGPT OdooBot.")) + + if OpenAI is None: + raise UserError(_("The OpenAI Python package is not installed. Please install 'openai'.")) + + client = OpenAI(api_key=api_key) + + try: + messages = [] + if context_text: + messages.append({"role": "system", "content": f"Conversation context:\n{context_text.strip()}"}) + messages.append({"role": "user", "content": body}) + + resp = client.chat.completions.create( + model="gpt-3.5-turbo", + messages=messages, + max_tokens=800, + temperature=0.7, + ) + reply = resp.choices[0].message.content or "" + except Exception as e: + raise UserError(_("OpenAI request failed: %s") % str(e)) + + gpt_html = "OpenAI: " + reply + + author_id = self.env.ref("base.partner_root").id + subtype_id = self.env.ref("mail.mt_comment").id + + self.env['mail.channel'].browse(record.id).message_post( + body=gpt_html, + message_type='comment', + subtype_id=subtype_id, + author_id=author_id, + ) + return gpt_html diff --git a/chatgpt_bot/models/res_config_setting.py b/chatgpt_bot/models/res_config_setting.py new file mode 100644 index 0000000..9ec7ac9 --- /dev/null +++ b/chatgpt_bot/models/res_config_setting.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models, fields + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + apikey = fields.Char( + string="API Key", + config_parameter="chatgpt_blog.apikey" + ) diff --git a/chatgpt_bot/models/res_users.py b/chatgpt_bot/models/res_users.py new file mode 100644 index 0000000..739266c --- /dev/null +++ b/chatgpt_bot/models/res_users.py @@ -0,0 +1,10 @@ +from odoo import api, fields, models, _ + + +class ResUsers(models.Model): + _inherit = 'res.users' + + odoobot_state = fields.Selection( + selection_add=[('chatgpt', 'ChatGPT')], + + ) \ No newline at end of file diff --git a/chatgpt_bot/security/ir.model.access.csv b/chatgpt_bot/security/ir.model.access.csv new file mode 100644 index 0000000..37ce15d --- /dev/null +++ b/chatgpt_bot/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_chatgpt_blog_chatgpt_blog,chatgpt_blog.chatgpt_blog,model_chatgpt_blog_chatgpt_blog,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/chatgpt_bot/static/description/banner.png b/chatgpt_bot/static/description/banner.png new file mode 100644 index 0000000..d6ff33f Binary files /dev/null and b/chatgpt_bot/static/description/banner.png differ diff --git a/chatgpt_bot/static/description/icon.png b/chatgpt_bot/static/description/icon.png new file mode 100644 index 0000000..fe52f1f Binary files /dev/null and b/chatgpt_bot/static/description/icon.png differ diff --git a/chatgpt_bot/static/description/images/banner.png b/chatgpt_bot/static/description/images/banner.png new file mode 100644 index 0000000..5799230 Binary files /dev/null and b/chatgpt_bot/static/description/images/banner.png differ diff --git a/chatgpt_bot/static/description/images/gif_chat.gif b/chatgpt_bot/static/description/images/gif_chat.gif new file mode 100644 index 0000000..2403faf Binary files /dev/null and b/chatgpt_bot/static/description/images/gif_chat.gif differ diff --git a/chatgpt_bot/static/description/images/image1.png b/chatgpt_bot/static/description/images/image1.png new file mode 100644 index 0000000..da579e6 Binary files /dev/null and b/chatgpt_bot/static/description/images/image1.png differ diff --git a/chatgpt_bot/static/description/images/image2.png b/chatgpt_bot/static/description/images/image2.png new file mode 100644 index 0000000..bcca92e Binary files /dev/null and b/chatgpt_bot/static/description/images/image2.png differ diff --git a/chatgpt_bot/static/description/images/image3.png b/chatgpt_bot/static/description/images/image3.png new file mode 100644 index 0000000..953294d Binary files /dev/null and b/chatgpt_bot/static/description/images/image3.png differ diff --git a/chatgpt_bot/static/description/images/image4.png b/chatgpt_bot/static/description/images/image4.png new file mode 100644 index 0000000..7ab9256 Binary files /dev/null and b/chatgpt_bot/static/description/images/image4.png differ diff --git a/chatgpt_bot/static/description/images/main_screenshot.png b/chatgpt_bot/static/description/images/main_screenshot.png new file mode 100644 index 0000000..8a5d3f3 Binary files /dev/null and b/chatgpt_bot/static/description/images/main_screenshot.png differ diff --git a/chatgpt_bot/static/description/index.html b/chatgpt_bot/static/description/index.html new file mode 100644 index 0000000..7c77196 --- /dev/null +++ b/chatgpt_bot/static/description/index.html @@ -0,0 +1,449 @@ + + + + + + +README.rst + + + +
+ + +
+

OpenAI ChatGPT Odoo Module

+

License: AGPL-3

+
+
This Odoo module allows for seamless integration with the OpenAI ChatGPT API,
+
providing advanced natural language processing capabilities within the Odoo platform. +With this module, users can leverage the power of ChatGPT to generate human-like text, +perform language translation, and more. +It utilizes API calls to communicate with the OpenAI service, +making it easy to set up and use.
+
+

Table of contents

+
+
+

Configuration

+ +
+
+

Bugfix

+ +
+
+

Usage

+

To use this module, you need to:

+ +images/image1.png +

This module is required for not blocking the server when the API is called.

+
+
+

Configuration users

+

inside the preferences of the user you can set the chatGPT bot as default bot.

+images/image2.png +

You can activate or deactivate the chatGPT bot for each user. +You can activate or deactivate the chatGPT directly in the chat window.

+

You can type: #enable or #disable

+images/image3.png +
+
+

Example

+images/image4.png +images/gif_chat.gif +
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • FL1 sro
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the FL1.

+Odoo Fl1 sro +
+
+
+ + diff --git a/chatgpt_bot/views/res_config_settings.xml b/chatgpt_bot/views/res_config_settings.xml new file mode 100644 index 0000000..375cbb6 --- /dev/null +++ b/chatgpt_bot/views/res_config_settings.xml @@ -0,0 +1,45 @@ + + + + + + res.config.settings.view.form.inherit.chatgpt + res.config.settings + + + + + +
+

ChatGPT OdooBot

+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + + ChatGPT Settings + res.config.settings + form + current + {'module': 'chatgpt_blog'} + + +
+
diff --git a/queue b/queue new file mode 160000 index 0000000..edc21e4 --- /dev/null +++ b/queue @@ -0,0 +1 @@ +Subproject commit edc21e4c4ef11a1ef746ca5ac641e9227602a35d