├── src
├── __pycache__
│ └── config.cpython-310.pyc
├── config.py
├── update.py
└── bot.py
├── LICENSE
└── README.md
/src/__pycache__/config.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TrippingLettuce/Shopify-Drop-Ship-Discord-Bot/main/src/__pycache__/config.cpython-310.pyc
--------------------------------------------------------------------------------
/src/config.py:
--------------------------------------------------------------------------------
1 | # Discord
2 |
3 | bot_key = 'Bot Key'
4 |
5 |
6 | # Mongo
7 |
8 | mongo_key = 'Mongo Key'
9 |
10 | database = 'ShopifyBot' #Can be anything really
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Caleb Klinger
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/update.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | import json
3 | from pymongo import MongoClient
4 | import time
5 | import urllib.parse
6 |
7 |
8 | client = MongoClient("Mongo Key")
9 | db = client["ShopifyBot"]
10 | r = 0
11 | #Inf loop with a sleep on it
12 | def getJSON(url):
13 | try:
14 | response = urlopen(url,timeout=20)
15 | except urllib.error.HTTPError as e:
16 | print("ERROR")
17 | print(e)
18 | data_json = json.loads(response.read())
19 | return data_json
20 |
21 |
22 | while True:
23 | r += 1
24 | print(f"\nRun #{r}\n")
25 | #Get all collections in mongo
26 | collection_names = db.list_collection_names()
27 |
28 | #Loop though collections
29 | for name in collection_names:
30 | #Set collections and URL for comparisions
31 | collection = db[f"{name}"]
32 | #documents Get
33 | document_count = collection.count_documents({})
34 | document_count = document_count - 1 #(dont count _id:0)
35 |
36 | counter = 1
37 | for x in range(document_count):
38 | prodData = collection.find_one({"_id": counter})
39 | title = prodData["title"]
40 | price = prodData["price"]
41 | handle_id = prodData["handle"]
42 | handle_id = urllib.parse.quote(handle_id)
43 | last_buy = prodData["bought"]
44 | url = f"https://{name}/products/{handle_id}/products.json"
45 | data_json = getJSON(url)
46 | last_sold = data_json["product"]['updated_at']
47 | counter += 1
48 | if last_buy != last_sold: #Item Bought
49 | counter -= 1
50 | print(f"{title} has been bought!")
51 | #New Buy Time, New Total Sales/Count Update
52 | prodData["bought"] = last_sold
53 | prodData["total_price"] += price
54 | prodData["total_sales"] += 1
55 | collection.replace_one({"_id":counter}, prodData)
56 | #New Total Sales(Week,Alltime) Update
57 | salesData = collection.find_one({"_id": 0})
58 | salesData["total"] += price
59 | salesData["week"] += price
60 | collection.replace_one({"_id":0}, salesData)
61 | time.sleep(30)
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Discord Shopify Tracking Bot
2 | *Tracking Example:*
3 | 
4 | Welcome to the Discord Shopify Tracking Bot repository!
5 | This bot allows you to track sales and data for your Shopify products and stores, and receive regular updates on their performance.
6 |
7 | ### Bot
8 | To use the bot, add the following to config.py Mongo Token, Discord Bot Token, Mongo DB name
9 | 0.5 Commands
10 | /track_product - Track a product(s) sales. Requires a product link.
11 | /check_product_data - Check total sales and data for a specific product. Requires a product link.
12 |
13 | Note: If you're tracking a new store, you must first use /track_product.
14 | 
15 |
16 | ### Tracker
17 | To use the tracker, add the following to Mongo Token, Mongo DB name
18 | This tracker will need to run 24/7 in the server. It runs in a while loop with a 30s sleep inbetween, lower sleep times may make mistakes.
19 | Run separately from bot
20 |
21 |
22 | ### Aditional Versions
23 | The 0.5 version has the basics and I will accept pull request if it can be made better, I have the finished 1.0 version and currently working on the 2.0 version
24 | I will publicly release the 1.0 when 2.0 is finished
25 |
26 |
27 | The full version 1.0 of the bot is available for purchase or customization. You can contact us on Discord or create an offer on Fiverr for more information.
28 | Here are some of the key features of version 1.0:
29 | Track multiple products from the same store
30 | Track stats of store-specific products with /check_store_data
31 | More accurate tracking up to 99.9% accurate
32 | Stop tracking after 30 days
33 | Better UI with a gif command (/help)
34 |
35 | I am also working on version 2.0, which will include even more features such as:
36 | User-specific products and stores
37 | Button scroll display for products
38 | Tracking entire stores
39 | Custom pings
40 | Tracking incoming products
41 | Even better UI
42 |
43 | ### Other
44 | Discord - Tripping_Lettuce#3780
45 | Fiverr - https://www.fiverr.com/tripping_lettuc
46 | *I also do custom bot related to AI API and Character AI*
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/bot.py:
--------------------------------------------------------------------------------
1 |
2 | import discord
3 | from discord.ext import commands
4 | from discord.ext.commands import Context
5 | from discord import app_commands
6 | from urllib.request import urlopen
7 | import json
8 | import pymongo
9 | from pymongo import MongoClient
10 | import datetime
11 | import config
12 |
13 | intents = discord.Intents.default()
14 | intents.members = True
15 | intents.message_content = True
16 | bot = commands.Bot(command_prefix='?', intents=intents)
17 |
18 |
19 | client = MongoClient(config.mongo_key)
20 | token = config.bot_key
21 | database = config.database
22 | db = client[database]
23 |
24 |
25 |
26 | #--- Bot Startup
27 | @bot.event
28 | async def on_ready():
29 | print(f'Logged in as {bot.user}')
30 |
31 |
32 | #Help CMD
33 | @bot.tree.command()
34 | async def help(interaction: discord.Interaction):
35 | """Help - Bot Commands"""
36 | #Webhook
37 | data = {
38 | "title": "Bot Commands",
39 | "description": "Use **/help** to bring up this menu anytime.",
40 | "color": 15856113,
41 | "fields": [
42 | {
43 | "name": "/track_product",
44 | "value": "Track a product(s) sales.\nRequires: `Product Link`"
45 | },
46 | {
47 | "name": "/check_product_data",
48 | "value": "Check total sales & data for product.\nRequires: `Product Link`"
49 | },
50 | {
51 | "name": "Product Link",
52 | "value": "EX: `https://juicleds.com/products/galaxy-projector`"
53 | },
54 | {
55 | "name": "/check_store_data (Unavailable version)",
56 | "value": "Check total sales & data for store\nRequires: `Website Link`"
57 | },
58 | ],
59 | "footer": {
60 | "text": "If you're tracking a new store, you must first use /track_product."
61 | },
62 | }
63 | embed = discord.Embed.from_dict(data)
64 | await interaction.response.send_message(embed = embed)
65 |
66 | @bot.tree.command()
67 | async def upgrade(interaction: discord.Interaction):
68 | """More Commands"""
69 | #Webhook
70 | data = {
71 | "title": "Get the latest version",
72 | "description": "The full 1.0 version is avaliable to purchase or even custom modified\nAdd me on discord or create an offer on fiverr for more information",
73 | "color": 15856113,
74 | "fields": [
75 | {
76 | "name": "Discord/Fiverr",
77 | "value": "`Tripping_Lettuce#3780`\n`https://www.fiverr.com/tripping_lettuc`"
78 | },
79 | {
80 | "name": "Version 1.0!",
81 | "value": "Track multiple products from same store!\nTrack stats of store specific products with `/check_store_data`\n More accurate tracking up to `99.9%` accurate\nStop tracking after 30 days\nBetter UI with gif `\help`"
82 | },
83 | {
84 | "name": "Version 2.0!!!",
85 | "value": "User Specfic Products and Stores\nButton scroll display for products\nTrack entire stores\nCustom Pings\nTrack incoming products\nEven better UI"
86 | },
87 |
88 | ],
89 | "footer": {
90 | "text": "All profits go to paying of my student loans :)"
91 | },
92 | "image": {
93 | "url": "https://cdn.discordapp.com/attachments/1062641874852122735/1080413136441577493/ezgif.com-video-to-gif.gif"
94 | }
95 | }
96 | embed = discord.Embed.from_dict(data)
97 | await interaction.response.send_message(embed = embed)
98 |
99 |
100 | @bot.tree.command()
101 | async def track_product(interaction: discord.Interaction,product_link:str):
102 | """Track a product(s) sales"""
103 |
104 | #Handel ID Get
105 | handle_id = product_link.split("/products/")[1]
106 | #Domain Get
107 | domain = product_link.split("/")[2]
108 | #Json Product Page Get
109 | url = f"https://{domain}/products/{handle_id}/products.json"
110 | #Open Json File Try
111 | try:
112 | response = urlopen(url)
113 | except:
114 | content = "There seems to be an error with the link use /help"
115 | await interaction.response.send_message(content=content, ephemeral=True)
116 | # Loads Json and Reads Try
117 | data_json = json.loads(response.read())
118 | #Send to function to organize and send to mongo Func
119 | data = organize(data_json,domain,handle_id)
120 | embed = discord.Embed.from_dict(data)
121 | await interaction.response.send_message(embed = embed)
122 |
123 |
124 |
125 |
126 | def organize(data_json: str, domain:str,handle_id:str):
127 |
128 |
129 | # Go to Mongo Collection
130 | collection = db[domain]
131 | #Started Tracking Get
132 | today = datetime.datetime.today()
133 | formatted_date = today.strftime('%m-%d-%y')
134 | document_count = collection.count_documents({})
135 |
136 | prod_handle = data_json["product"]['handle']
137 |
138 |
139 | if document_count == 2:
140 | prodData = collection.find_one({"_id": 1})
141 | db_handle = prodData["handle"]
142 | if db_handle == prod_handle:
143 | data = {
144 | "title": "This product is already being tracked",
145 | "description": "Use /check_product_data to check total sales & data for this product.\nRequires: `Product Link`",
146 | "color": 880808,
147 | }
148 | return data
149 |
150 | collection.insert_one({"_id":0,"track_start":formatted_date,"total":0.00,"week":0.00}) #time stamps to track time
151 |
152 | prod_name = data_json["product"]['title']
153 | prod_bought = data_json["product"]['updated_at']
154 | prod_post = data_json["product"]['published_at']
155 |
156 | #Product Sales,Ammount Set
157 | sold_total = 0
158 | sold_prod = 0
159 |
160 | #Product Image Set
161 | src = data_json["product"]["images"][0]["src"]
162 |
163 | #Price from variants Get
164 | prod_price = data_json["product"]["variants"][0]["price"]
165 | prod_price = float(prod_price) #Convert to int
166 |
167 | #Put into mongo Push
168 | collection.insert_one({"_id":1,"title":prod_name,"handle":prod_handle,"bought":prod_bought,"post":prod_post,"image":src,"price":prod_price, "total_price":sold_total,"total_sales":sold_prod,"track_start":formatted_date})
169 |
170 | url = "https://" + domain
171 |
172 | data = {
173 | "title": domain,
174 | "description": "Allow 24 hours for data to populate.",
175 | "url": url,
176 | "color": 15856113,
177 | "fields": [
178 | {
179 | "name": "/track_product",
180 | "value": "Track a product sales.\nRequires: `Product Link`"
181 | },
182 | {
183 | "name": "/check_product_data",
184 | "value": "Check total sales & data for product.\nRequires: `Product Link`"
185 | },
186 | {
187 | "name": "/help",
188 | "value": "Show the list of commands."
189 | }
190 | ],
191 | "author": {
192 | "name": "Product Tracking Started"
193 | }
194 | }
195 | return data
196 |
197 |
198 |
199 | @bot.tree.command()
200 | async def check_product_data(interaction: discord.Interaction, product_link:str):
201 | """Check total sales & data for product"""
202 |
203 | #Handel ID Get
204 | handle_id = product_link.split("/products/")[1]
205 | #Domain Get
206 | domain = product_string = product_link.split("/")[2]
207 | #Get mongo colelction Get
208 | collection = db[domain]
209 |
210 |
211 |
212 | prodData = collection.find_one({"_id": 1})
213 |
214 |
215 | #----Get Top Data-----
216 | title = prodData["title"]
217 | domain = domain.replace("/", "")
218 | handle = prodData["handle"]
219 | src = prodData["image"]
220 | prod_price = prodData["total_price"]
221 | prod_price = str(prod_price)
222 | prod_sales = prodData["total_sales"]
223 | track_start = prodData["track_start"]
224 | prod_pricessss = prodData["price"]
225 | #Get Post Date
226 | prod_post = prodData["post"]
227 | prod_post = prod_post.split("T")[0]
228 |
229 | prod_buy = prodData["bought"]
230 | prod_buy1 = prod_buy.split("T")[0]
231 | prod_buy2 = prod_buy.split("T")[1]
232 | prod_buy2 = prod_buy2.split("-")[0]
233 | prod_buy = f"{prod_buy1} : {prod_buy2}"
234 |
235 | product_link = "https://" + domain + '/products/' + handle
236 |
237 |
238 | data = {
239 | "title": title,
240 | "url": product_link,
241 | "color": 15856113,
242 | "fields": [
243 | {
244 | "name": "Product Tracking Since",
245 | "value": track_start,
246 | "inline": True
247 | },
248 | {
249 | "name": "Total Sales",
250 | "value": f"${prod_price}",
251 | "inline": True
252 | },
253 | {
254 | "name": "7 Day Sales",
255 | "value": f"${prod_price}",
256 | "inline": True
257 | },
258 | {
259 | "name": "Product Data",
260 | "value": f"-----------------------------\n**Name - {title}\nLast Sold - {prod_buy}\nCreated - {prod_post}\nPrice - {prod_pricessss}**\n-----------------------------"
261 | },
262 | ],
263 | "author": {
264 | "name": "Product Sales Data"
265 | },
266 | "footer": {
267 | "text": "Tracking stops if not searched in 30 days, or no sales in 3 days."
268 | },
269 | "thumbnail": {
270 | "url": src
271 | }
272 | }
273 |
274 | embed = discord.Embed.from_dict(data)
275 | await interaction.response.send_message(embed = embed)
276 |
277 |
278 | #------ Sync Command ------
279 | @bot.command()
280 | @commands.guild_only()
281 | @commands.is_owner()
282 | async def sync(ctx: Context):
283 | synced = await ctx.bot.tree.sync()
284 | await ctx.send(f"Synced {len(synced)} commands {'globally'}")
285 |
286 |
287 | bot.run(token)
288 |
289 |
--------------------------------------------------------------------------------