1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/social_facebook/models/social_stream.py
2024-12-10 09:04:09 +07:00

202 lines
8.8 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
import dateutil.parser
import requests
from odoo import models, fields, api
from werkzeug.urls import url_join
class SocialStreamFacebook(models.Model):
_inherit = 'social.stream'
def _apply_default_name(self):
facebook_streams = self.filtered(lambda s: s.media_id.media_type == 'facebook')
super(SocialStreamFacebook, (self - facebook_streams))._apply_default_name()
for stream in facebook_streams:
stream.write({'name': '%s: %s' % (stream.stream_type_id.name, stream.account_id.name)})
def _fetch_stream_data(self):
if self.media_id.media_type != 'facebook':
return super(SocialStreamFacebook, self)._fetch_stream_data()
if self.stream_type_id.stream_type == 'facebook_page_posts':
return self._fetch_page_posts('published_posts')
elif self.stream_type_id.stream_type == 'facebook_page_mentions':
return self._fetch_page_posts('tagged')
def _fetch_page_posts(self, endpoint_name):
self.ensure_one()
facebook_fields = [
'id',
'message',
'from',
'shares',
'likes.limit(1).summary(true)',
'comments.limit(10).summary(true){message,from,like_count}',
'attachments',
'created_time',
'message_tags'
]
if endpoint_name == 'published_posts':
facebook_fields.append('insights.metric(post_impressions)')
posts_endpoint_url = url_join(self.env['social.media']._FACEBOOK_ENDPOINT_VERSIONED, "%s/%s" % (self.account_id.facebook_account_id, endpoint_name))
result = requests.get(posts_endpoint_url,
params={
'access_token': self.account_id.facebook_access_token,
'fields': ','.join(facebook_fields)
},
timeout=5
)
result_posts = result.json().get('data')
if not result_posts:
self.account_id._action_disconnect_accounts(result.json())
return False
facebook_post_ids = [post.get('id') for post in result_posts]
existing_posts = self.env['social.stream.post'].search([
('stream_id', '=', self.id),
('facebook_post_id', 'in', facebook_post_ids)
])
existing_posts_by_facebook_post_id = {
post.facebook_post_id: post for post in existing_posts
}
posts_to_create = []
for post in result_posts:
values = {
'stream_id': self.id,
'message': self._format_facebook_message(post.get('message'), post.get('message_tags')),
'author_name': post.get('from', {}).get('name', ''),
'facebook_author_id': post.get('from', {}).get('id'),
'published_date': dateutil.parser.parse(post.get('created_time'), ignoretz=True),
'facebook_shares_count': post.get('shares', {}).get('count'),
'facebook_likes_count': post.get('likes', {}).get('summary', {}).get('total_count'),
'facebook_user_likes': post.get('likes', {}).get('summary', {}).get('has_liked'),
'facebook_comments_count': post.get('comments', {}).get('summary', {}).get('total_count'),
'facebook_reach': post.get('insights', {}).get('data', [{}])[0].get('values', [{}])[0].get('value'),
'facebook_post_id': post.get('id'),
'facebook_is_event_post': post.get('attachments', {}).get('data', [{}])[0].get('type') == 'event',
}
attachments = self._extract_facebook_attachments(post)
existing_post = existing_posts_by_facebook_post_id.get(post.get('id'))
if existing_post:
if attachments.get('stream_post_image_ids'):
values['stream_post_image_ids'] = [(5, 0, 0)] + attachments['stream_post_image_ids']
existing_post.sudo().write(values)
else:
if attachments or values['message']:
# do not create post without content
values.update(attachments)
posts_to_create.append(values)
stream_posts = self.env['social.stream.post'].sudo().create(posts_to_create)
return any(stream_post.stream_id.create_uid.id == self.env.uid for stream_post in stream_posts)
@api.model
def _extract_facebook_attachments(self, post):
result = {}
for attachment in post.get('attachments', {}).get('data', []):
if attachment.get('type') == 'share':
result.update({
'link_title': attachment.get('title'),
'link_description': attachment.get('description'),
'link_url': attachment.get('url'),
})
if attachment.get('media'):
result.update({
'link_image_url': attachment.get('media').get('image').get('src')
})
elif attachment.get('type') == 'album':
images = []
images_urls = []
for sub_image in attachment.get('subattachments', {}).get('data', []):
image_url = sub_image.get('media').get('image').get('src')
images.append({
'image_url': image_url
})
images_urls.append(image_url)
if images:
result.update({
'stream_post_image_ids': [(0, 0, attachment) for attachment in images],
})
elif attachment.get('type') in ['photo', 'animated_image_video']:
# TODO improvement later: handle videos in Feed view to correctly display FB GIFs?
image_src = attachment.get('media', {}).get('image', {}).get('src')
if image_src:
result.update({'stream_post_image_ids': [(0, 0, {'image_url': image_src})]})
elif attachment.get('type') == 'event':
# events creation post are handle like link and image in the frontend
result.update({
'link_title': attachment.get('title'),
'link_description': attachment.get('description'),
'link_url': attachment.get('target', {}).get('url', ''),
})
image_src = attachment.get('media', {}).get('image', {}).get('src')
if image_src:
result.update({'stream_post_image_ids': [(0, 0, {'image_url': image_src})]})
return result
@api.model
def _format_facebook_message(self, message, tags):
"""
This allows to create links to the referenced Facebook pages
example:
"Hello Odoo :)" -> "Hello @[542132] Odoo :)"
"Hello Odoo Social :)" -> "Hello @[542132] Odoo-Social :)"
"Hello Odoo - Social :)" -> "Hello @[542132] Odoo-Social :)"
details:
"Hello @[42] faketag Odoo - Social :)" -> "Hello @ [42] faketag @[542132] Odoo-Social :)""
-> With associated tag: {name: "Odoo - Social", offset: 20, length: 13}
We take the message until the tag offset: "Hello @[42] faketag "
and remove the fake tag in the process "Hello @ [42] faketag "
Then we insert the forged tag "@[542132] Odoo-Social"
Then take the rest of the message, starting at index tag offset + length, since we inserted the forged tag instead
:param message: str message
:param tags: list of tags in the message as received through Facebook endpoint
- id: ID of the page/user/group tagged
- name: name of the page/user/group tagged
- offset: position of the tag in the text
- length: length of the tag in the text
"""
if not message:
return message
def remove_forged_tags(message):
"""
Remove False positive in the message
(e.g: if someone write "@[42] test" without tagging someone)
"""
return re.sub(r'\B@\[', '@ [', message)
# Remove Facebook hashtags that have are considered as tags with ID but are not managed by this method
tags = [tag for tag in tags or [] if not tag.get('name', '').startswith('#')]
message_index = 0
message_with_tags = ''
for tag in tags or []:
no_space_name = re.sub(r'\s+', '-', re.sub(r'\s*-\s*', '-', tag['name']))
forged_tag = '@[%s] %s' % (tag['id'], no_space_name)
message_with_tags += remove_forged_tags(message[message_index:tag['offset']]) + forged_tag
message_index = tag['offset'] + tag['length']
message_with_tags += remove_forged_tags(message[message_index:])
return message_with_tags