refactor!: remove twitter integration
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.js b/erpnext/crm/doctype/social_media_post/social_media_post.js
index d4ac0ba..71d044c 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.js
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.js
@@ -2,7 +2,7 @@
 // For license information, please see license.txt
 frappe.ui.form.on('Social Media Post', {
 	validate: function(frm) {
-		if (frm.doc.twitter === 0 && frm.doc.linkedin === 0) {
+		if (frm.doc.linkedin === 0) {
 			frappe.throw(__("Select atleast one Social Media Platform to Share on."));
 		}
 		if (frm.doc.scheduled_time) {
@@ -45,13 +45,6 @@
 					}
 
 					let datasets = [], colors = [];
-					if (r.message && r.message.twitter) {
-						colors.push('#1DA1F2');
-						datasets.push({
-							name: 'Twitter',
-							values: [r.message.twitter.favorite_count, r.message.twitter.retweet_count]
-						});
-					}
 					if (r.message && r.message.linkedin) {
 						colors.push('#0077b5');
 						datasets.push({
@@ -104,13 +97,6 @@
 
 			if (frm.doc.post_status !='Deleted') {
 				let html='';
-				if (frm.doc.twitter) {
-					let color = frm.doc.twitter_post_id ? "green" : "red";
-					let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted";
-					html += `<div class="col-xs-6">
-								<span class="indicator whitespace-nowrap ${color}"><span>Twitter : ${status} </span></span>
-							</div>` ;
-				}
 				if (frm.doc.linkedin) {
 					let color = frm.doc.linkedin_post_id ? "green" : "red";
 					let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted";
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json
index 98e78f9..1e3e01c 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.json
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.json
@@ -12,14 +12,8 @@
   "scheduled_time",
   "post_status",
   "column_break_6",
-  "twitter",
   "linkedin",
-  "twitter_post_id",
   "linkedin_post_id",
-  "content",
-  "text",
-  "column_break_14",
-  "tweet_preview",
   "linkedin_section",
   "linkedin_post",
   "column_break_15",
@@ -29,24 +23,12 @@
  ],
  "fields": [
   {
-   "fieldname": "text",
-   "fieldtype": "Small Text",
-   "label": "Tweet",
-   "mandatory_depends_on": "eval:doc.twitter ==1"
-  },
-  {
    "fieldname": "image",
    "fieldtype": "Attach Image",
    "label": "Image"
   },
   {
    "default": "1",
-   "fieldname": "twitter",
-   "fieldtype": "Check",
-   "label": "Twitter"
-  },
-  {
-   "default": "1",
    "fieldname": "linkedin",
    "fieldtype": "Check",
    "label": "LinkedIn"
@@ -61,12 +43,6 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval:doc.twitter ==1",
-   "fieldname": "content",
-   "fieldtype": "Section Break",
-   "label": "Twitter"
-  },
-  {
    "allow_on_submit": 1,
    "fieldname": "post_status",
    "fieldtype": "Select",
@@ -77,15 +53,6 @@
   },
   {
    "allow_on_submit": 1,
-   "fieldname": "twitter_post_id",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Twitter Post Id",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
-   "allow_on_submit": 1,
    "fieldname": "linkedin_post_id",
    "fieldtype": "Data",
    "hidden": 1,
@@ -106,14 +73,6 @@
    "label": "Share On"
   },
   {
-   "fieldname": "column_break_14",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "tweet_preview",
-   "fieldtype": "HTML"
-  },
-  {
    "collapsible": 1,
    "depends_on": "eval:doc.linkedin==1",
    "fieldname": "linkedin_section",
@@ -152,10 +111,11 @@
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-14 14:24:59.821223",
+ "modified": "2023-09-14 11:24:29.105683",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Social Media Post",
+ "naming_rule": "Expression",
  "owner": "Administrator",
  "permissions": [
   {
@@ -203,6 +163,7 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.py b/erpnext/crm/doctype/social_media_post/social_media_post.py
index 3654d29..9615a83 100644
--- a/erpnext/crm/doctype/social_media_post/social_media_post.py
+++ b/erpnext/crm/doctype/social_media_post/social_media_post.py
@@ -11,7 +11,7 @@
 
 class SocialMediaPost(Document):
 	def validate(self):
-		if not self.twitter and not self.linkedin:
+		if not self.linkedin:
 			frappe.throw(_("Select atleast one Social Media Platform to Share on."))
 
 		if self.scheduled_time:
@@ -33,10 +33,6 @@
 
 	@frappe.whitelist()
 	def delete_post(self):
-		if self.twitter and self.twitter_post_id:
-			twitter = frappe.get_doc("Twitter Settings")
-			twitter.delete_tweet(self.twitter_post_id)
-
 		if self.linkedin and self.linkedin_post_id:
 			linkedin = frappe.get_doc("LinkedIn Settings")
 			linkedin.delete_post(self.linkedin_post_id)
@@ -49,19 +45,11 @@
 		if self.linkedin and self.linkedin_post_id:
 			linkedin = frappe.get_doc("LinkedIn Settings")
 			response["linkedin"] = linkedin.get_post(self.linkedin_post_id)
-		if self.twitter and self.twitter_post_id:
-			twitter = frappe.get_doc("Twitter Settings")
-			response["twitter"] = twitter.get_tweet(self.twitter_post_id)
-
 		return response
 
 	@frappe.whitelist()
 	def post(self):
 		try:
-			if self.twitter and not self.twitter_post_id:
-				twitter = frappe.get_doc("Twitter Settings")
-				twitter_post = twitter.post(self.text, self.image)
-				self.db_set("twitter_post_id", twitter_post.id)
 			if self.linkedin and not self.linkedin_post_id:
 				linkedin = frappe.get_doc("LinkedIn Settings")
 				linkedin_post = linkedin.post(self.linkedin_post, self.title, self.image)
diff --git a/erpnext/crm/doctype/twitter_settings/__init__.py b/erpnext/crm/doctype/twitter_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/crm/doctype/twitter_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py b/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
deleted file mode 100644
index 9dbce8f..0000000
--- a/erpnext/crm/doctype/twitter_settings/test_twitter_settings.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-
-# import frappe
-import unittest
-
-
-class TestTwitterSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.js b/erpnext/crm/doctype/twitter_settings/twitter_settings.js
deleted file mode 100644
index c322092..0000000
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Twitter Settings', {
-	onload: function(frm) {
-		if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret){
-			frappe.confirm(
-				__('Session not valid, Do you want to login?'),
-				function(){
-					frm.trigger("login");
-				},
-				function(){
-					window.close();
-				}
-			);
-		}
-		frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings'>${__('click here')}</a>`]));
-	},
-	refresh: function(frm) {
-		let msg, color, flag=false;
-		if (frm.doc.session_status == "Active") {
-			msg = __("Session Active");
-			color = 'green';
-			flag = true;
-		}
-		else if(frm.doc.consumer_key && frm.doc.consumer_secret) {
-			msg = __("Session Not Active. Save doc to login.");
-			color = 'red';
-			flag = true;
-		}
-
-		if (flag) {
-			frm.dashboard.set_headline_alert(
-				`<div class="row">
-					<div class="col-xs-12">
-						<span class="indicator whitespace-nowrap ${color}"><span class="hidden-xs">${msg}</span></span>
-					</div>
-				</div>`
-			);
-		}
-	},
-	login: function(frm) {
-		if (frm.doc.consumer_key && frm.doc.consumer_secret){
-			frappe.dom.freeze();
-			frappe.call({
-				doc: frm.doc,
-				method: "get_authorize_url",
-				callback : function(r) {
-					window.location.href = r.message;
-				}
-			}).fail(function() {
-				frappe.dom.unfreeze();
-			});
-		}
-	},
-	after_save: function(frm) {
-		frm.trigger("login");
-	}
-});
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
deleted file mode 100644
index 8d05877..0000000
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json
+++ /dev/null
@@ -1,102 +0,0 @@
-{
- "actions": [],
- "creation": "2020-01-30 10:29:08.562108",
- "doctype": "DocType",
- "documentation": "https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
-  "account_name",
-  "profile_pic",
-  "oauth_details",
-  "consumer_key",
-  "column_break_5",
-  "consumer_secret",
-  "access_token",
-  "access_token_secret",
-  "session_status"
- ],
- "fields": [
-  {
-   "fieldname": "account_name",
-   "fieldtype": "Data",
-   "label": "Account Name",
-   "read_only": 1
-  },
-  {
-   "fieldname": "oauth_details",
-   "fieldtype": "Section Break",
-   "label": "OAuth Credentials"
-  },
-  {
-   "fieldname": "consumer_key",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "API Key",
-   "reqd": 1
-  },
-  {
-   "fieldname": "consumer_secret",
-   "fieldtype": "Password",
-   "in_list_view": 1,
-   "label": "API Secret Key",
-   "reqd": 1
-  },
-  {
-   "fieldname": "column_break_5",
-   "fieldtype": "Column Break"
-  },
-  {
-   "fieldname": "profile_pic",
-   "fieldtype": "Attach Image",
-   "hidden": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "session_status",
-   "fieldtype": "Select",
-   "hidden": 1,
-   "label": "Session Status",
-   "options": "Expired\nActive",
-   "read_only": 1
-  },
-  {
-   "fieldname": "access_token",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Access Token",
-   "read_only": 1
-  },
-  {
-   "fieldname": "access_token_secret",
-   "fieldtype": "Data",
-   "hidden": 1,
-   "label": "Access Token Secret",
-   "read_only": 1
-  }
- ],
- "image_field": "profile_pic",
- "issingle": 1,
- "links": [],
- "modified": "2021-02-18 15:18:07.900031",
- "modified_by": "Administrator",
- "module": "CRM",
- "name": "Twitter Settings",
- "owner": "Administrator",
- "permissions": [
-  {
-   "create": 1,
-   "delete": 1,
-   "email": 1,
-   "print": 1,
-   "read": 1,
-   "role": "System Manager",
-   "share": 1,
-   "write": 1
-  }
- ],
- "quick_entry": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
deleted file mode 100644
index 442aa77..0000000
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import json
-
-import frappe
-import tweepy
-from frappe import _
-from frappe.model.document import Document
-from frappe.utils import get_url_to_form
-from frappe.utils.file_manager import get_file_path
-
-
-class TwitterSettings(Document):
-	@frappe.whitelist()
-	def get_authorize_url(self):
-		callback_url = (
-			"{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(
-				frappe.utils.get_url()
-			)
-		)
-		auth = tweepy.OAuth1UserHandler(
-			self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url
-		)
-		try:
-			redirect_url = auth.get_authorization_url()
-			return redirect_url
-		except (tweepy.TweepyException, tweepy.HTTPException) as e:
-			frappe.msgprint(_("Error! Failed to get request token."))
-			frappe.throw(
-				_("Invalid {0} or {1}").format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key"))
-			)
-
-	def get_access_token(self, oauth_token, oauth_verifier):
-		auth = tweepy.OAuth1UserHandler(
-			self.consumer_key, self.get_password(fieldname="consumer_secret")
-		)
-		auth.request_token = {"oauth_token": oauth_token, "oauth_token_secret": oauth_verifier}
-
-		try:
-			auth.get_access_token(oauth_verifier)
-			self.access_token = auth.access_token
-			self.access_token_secret = auth.access_token_secret
-			api = self.get_api()
-			user = api.me()
-			profile_pic = (user._json["profile_image_url"]).replace("_normal", "")
-
-			frappe.db.set_value(
-				self.doctype,
-				self.name,
-				{
-					"access_token": auth.access_token,
-					"access_token_secret": auth.access_token_secret,
-					"account_name": user._json["screen_name"],
-					"profile_pic": profile_pic,
-					"session_status": "Active",
-				},
-			)
-
-			frappe.local.response["type"] = "redirect"
-			frappe.local.response["location"] = get_url_to_form("Twitter Settings", "Twitter Settings")
-		except (tweepy.TweepyException, tweepy.HTTPException) as e:
-			frappe.msgprint(_("Error! Failed to get access token."))
-			frappe.throw(_("Invalid Consumer Key or Consumer Secret Key"))
-
-	def get_api(self):
-		# authentication of consumer key and secret
-		auth = tweepy.OAuth1UserHandler(
-			self.consumer_key, self.get_password(fieldname="consumer_secret")
-		)
-		# authentication of access token and secret
-		auth.set_access_token(self.access_token, self.access_token_secret)
-
-		return tweepy.API(auth)
-
-	def post(self, text, media=None):
-		if not media:
-			return self.send_tweet(text)
-
-		if media:
-			media_id = self.upload_image(media)
-			return self.send_tweet(text, media_id)
-
-	def upload_image(self, media):
-		media = get_file_path(media)
-		api = self.get_api()
-		media = api.media_upload(media)
-
-		return media.media_id
-
-	def send_tweet(self, text, media_id=None):
-		api = self.get_api()
-		try:
-			if media_id:
-				response = api.update_status(status=text, media_ids=[media_id])
-			else:
-				response = api.update_status(status=text)
-
-			return response
-
-		except (tweepy.TweepyException, tweepy.HTTPException) as e:
-			self.api_error(e)
-
-	def delete_tweet(self, tweet_id):
-		api = self.get_api()
-		try:
-			api.destroy_status(tweet_id)
-		except (tweepy.TweepyException, tweepy.HTTPException) as e:
-			self.api_error(e)
-
-	def get_tweet(self, tweet_id):
-		api = self.get_api()
-		try:
-			response = api.get_status(tweet_id, trim_user=True, include_entities=True)
-		except (tweepy.TweepyException, tweepy.HTTPException) as e:
-			self.api_error(e)
-
-		return response._json
-
-	def api_error(self, e):
-		content = json.loads(e.response.content)
-		content = content["errors"][0]
-		if e.response.status_code == 401:
-			self.db_set("session_status", "Expired")
-			frappe.db.commit()
-		frappe.throw(
-			content["message"],
-			title=_("Twitter Error {0} : {1}").format(e.response.status_code, e.response.reason),
-		)
-
-
-@frappe.whitelist(allow_guest=True)
-def callback(oauth_token=None, oauth_verifier=None):
-	if oauth_token and oauth_verifier:
-		twitter_settings = frappe.get_single("Twitter Settings")
-		twitter_settings.get_access_token(oauth_token, oauth_verifier)
-		frappe.db.commit()
-	else:
-		frappe.local.response["type"] = "redirect"
-		frappe.local.response["location"] = get_url_to_form("Twitter Settings", "Twitter Settings")
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index d0ee2e4..22c3715 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -339,6 +339,7 @@
 erpnext.patches.v15_0.remove_exotel_integration
 erpnext.patches.v14_0.single_to_multi_dunning
 execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0)
+execute:frappe.delete_doc('DocType', 'Twitter Settings', ignore_missing=True)
 erpnext.patches.v15_0.correct_asset_value_if_je_with_workflow
 erpnext.patches.v15_0.delete_woocommerce_settings_doctype
 # below migration patch should always run last