Skip to content
Health
60/100
fair
  • Security25/25
    No findings
  • Activity25/25
    Human commit this month
  • Required by5/25
    0 modules depend on it
  • Maturity5/25
    In 1 Odoo series since 17.0

Heuristic over the facts below, not an audit · How it's computed

Security findings
Grave / minor (static analysis)
Required by
0
Modules depending on it
External deps
2 pip · 0 bin
Python packages / system binaries
License
AGPL-3
No conflicts with its dependencies
Folder size
4.08 MB
Module directory on disk
Code analysis 0 migration notes 6 models 3 views 1 HTTP endpoint

Description

This module provides the necessary functionality for basic interaction with the X social media.

Main features:

  • User account integration.
  • Post creation.
  • Reports with agnostic metrics, on the account card of the Dashboard. The figures are read back from X by the connector itself: the publications of the last 30 days are refreshed once a day, and the card of the account adds up the counters its publications hold. A synchronization module widens what is known — the publications Odoo did not send, and the history older than that window — but the card is not empty without one.
  • Reposts and quotes, the two figures only X reports. They are shown as Reposts and Quotes in the Statistics dialog of a publication, opened from the menu of its card on the Dashboard, and they count towards the Interactions total and the Engagement rate of the publication and of the account.
  • What X will not publish, shown on the post while it is written. The message is checked against the characters the plan of the account allows, 280 without X Premium and 25 000 with it, and the medias against 4 images of at most 5 MB each — 15 MB for a GIF — in JPG, PNG, WEBP or GIF, and one video of at most 512 MB in MP4. X takes images or a video, never both in the same post, so a post mixing them is refused instead of warned about. The same checks refuse the publication if the post reaches it anyway, through an import or an RPC call.
  • The credit balance of the developer App that pays the calls to the X API, on the account form. It is read from X every 2 hours and from the buttons of the account, and the responsible of the account is warned when it drops under a threshold set on the account or is used up.

An X account will never draw a time series in Social Media > Statistics. That screen reads a history per day, and the metrics the API v2 answers are the current lifetime counters of each post, with no way of asking for them grouped by day. Giving X a series means either repeating the lifetime counter on every day, which draws a cumulative curve of a different shape than the other social media on the same graph, or storing the delta between two readings, which is false for any day the scheduled action did not run. Neither is a measurement, so nothing is written: an account of X shows the standard empty view there, and its figures live on its card of the Dashboard, which is where they mean something.

Dependencies

Odoo dependencies
OCA/social:
odoo/odoo:
- utm
- web
- bus
Python dependencies
requests_oauthlib, tweepy
System dependencies
None
Required by
None

Code analysis

Extracted from the source by best-effort static analysis (no code is executed); details may be incomplete.

Views touched 3

XML IDNameModelTypeStatus
social_account_view_form_inherit social.account.view.form.inherit.x social.account form Inherits social_media_base.social_account_view_form
social_post_account_view_form_statistics_inherit social.post.account.view.form.statistics.inherit.social.media.x social.post.account form Inherits social_media_base.social_post_account_view_form_statistics
wizard_social_account_view_form_inherit wizard.social.account.view.form.inherit.x wizard.social.account form Inherits social_media_base.wizard_social_account_view_form

HTTP endpoints 1

Route(s)HandlerAuthTypeMethodsFlags
/social_x/callback SocialMediaX.social_x user http ALL sudo

Models touched 6

New fields (11)
  • rate_limit_endpoint Json
    copy=False default=dict
  • x_access_secret_oauth1 Char
    groups='base.group_system'
  • x_access_token_oauth1 Char
    groups='base.group_system' help='Post and delete' string='Token for write'
  • x_access_token_oauth2 Char
    groups='base.group_system' help='App-only bearer token, obtained from the API Key and the API Secret. It authorizes what is asked in the name of the App and not of a user.' string='Token for read'
  • x_api_key Char
    groups='base.group_system' help='The API Key of the developer App, which the X Developer Console lists under OAuth 1.0 Keys as the Consumer Key. Regenerating it in the Console invalidates the one stored here.' string='API Key'
  • x_api_secret Char
    groups='base.group_system' help='The API Key Secret of the developer App, shown by the X Developer Console only when the Consumer Key is generated. Regenerating it in the Console invalidates the one stored here.' string='API Secret'
  • x_credit_balance Float
    copy=False digits=(16, 2) help='What is left of the credits the developer App bought to pay for the calls to the X API. It is the balance of the App and not of this account, so the accounts holding the same API Key share it. It is read again every 2 hours and when the account is associated, updated or its statistics refreshed.' readonly=True string='Credit Balance (USD)'
  • x_credit_balance_date Datetime
    copy=False help='When X last answered the credit balance.' readonly=True string='Credit Balance Read On'
  • x_credit_warned Boolean
    copy=False help='Whether the responsible was already warned that the credit balance is under the warning threshold. Cleared when the balance is read again at or over it, so the warning is given once per drop.' readonly=True
  • x_credit_warning Float
    default=1.0 digits=(16, 2) help='The responsible of the account is warned when the credit balance drops under this amount. At 0, only when it is used up.' string='Credit Warning (USD)'
  • x_premium Boolean
    copy=False default=False help='Whether the X user of this account holds an X Premium subscription, which raises the message of a post from 280 to 25 000 characters. Read from X when the account is associated and on Update account, so a subscription bought or cancelled later is seen after pressing Update account.' readonly=True
Public methods (5)
  • action_refresh_statistics(self)
    Read the credit balance of the App as well, for an X account.
  • action_update_account(self)
  • create_account_x(self, x_access_token_oauth1, x_access_secret_oauth1, kwargs)
    Create or update the account of the authorized X user. An existing account is only reused when the current user is allowed to associate it, and it is reactivated if it was archived.
  • create_tweet(self, message, image_ids, video_ids, post_id, post_account_id)
    Publish a post with its media. The rate limit is answered here, because it is not a failure of the post but a time to wait, and the user is told when to try again. Everything else is left to the caller: the publication guard records it on the line, with its reason, which is where the user looks for it afterwards. A ``401`` is raised as a credentials error: X answers it for a token it no longer accepts, and since X has no way to renew the token from Odoo, the account is flagged for the user to authorize it again. A ``400`` or a ``403`` is a post X refuses, not a token it rejects: the cause is the plan of the App, the plan of the account, the permissions of the App or the content, and none of them is fixed by authorizing the account again. They are raised as a user error with the reason told by :meth:`~._x_refused_post_message`, and the account is left as it is. The upload of the medias runs inside the same guard, so a media X refuses is explained the same way. The references of the medias travel back with the identifier of the tweet, so the publication stores in one write what it published and what X called each of its medias. :return: The id of the published tweet or False, and the ``media_id`` of each media keyed by the identifier of its attachment. :rtype: tuple
  • get_client_api(self, client_api=True, x_access_token_oauth1=None, x_access_secret_oauth1=None, bearer_token=None, kwargs=None)
    Build the tweepy client for this account. ``client_api`` returns the API v2 client, which serves every endpoint of X but the upload of medias; otherwise the v1.1 client, still needed for that upload. :rtype: tweepy.Client | tweepy.API

New fields (1)
  • media_type Selection
    ondelete={'x': 'cascade'} selection_add=[('x', 'X')]
Public methods (1)
  • action_open_account(self)

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (0)

No new fields.

Public methods (0)

No public methods.

New fields (2)
  • quote_count Integer
    default=0
  • retweet_count Integer
    default=0
Public methods (0)

No public methods.

New fields (3)
  • oauth_token Char
  • x_api_key Char
    help='The API Key of the developer App, which the X Developer Console lists under OAuth 1.0 Keys as the Consumer Key. Regenerating it in the Console invalidates the one stored here.' string='API Key'
  • x_api_secret Char
    help='The API Key Secret of the developer App, shown by the X Developer Console only when the Consumer Key is generated. Regenerating it in the Console invalidates the one stored here.' string='API Secret'
Public methods (0)

No public methods.

Loading…