2020)
2121
2222
23- async def convert (self , link , alias = None , silently = False , timeout = 10 ):
23+ async def adlinkfy_convert (self , link , alias = None , silently = False , timeout = 10 ):
2424 """
2525 Shorten a URL using Link Shortly/All Adlinkfy API.
2626
@@ -86,4 +86,252 @@ async def convert(self, link, alias=None, silently=False, timeout=10):
8686 except aiohttp .ClientConnectionError :
8787 raise ShortlyConnectionError (f"Failed to connect to { self .base_url } ." )
8888 except Exception as e :
89- raise ShortlyError (f"An unexpected error occurred: { e } " )
89+ raise ShortlyError (f"An unexpected error occurred: { e } " )
90+
91+ async def shareus_convert (self , link , alias = None , silently = False , timeout = 10 ):
92+ """
93+ Shorten a URL using Shareus.io API.
94+
95+ Parameters:
96+ api_key (str): Your API key for the Shareus service.
97+ base_url (str): The domain of the API (should be "shareus.io").
98+ link (str): The long URL you want to shorten.
99+ alias (str, optional): Custom alias for the short link. Default is None.
100+ silently (bool): If True, the function will directly return the original URL without raising errors.
101+ timeout (int, optional): Maximum seconds to wait for API response. Default is 10.
102+
103+ Returns:
104+ str: The shortened URL returned by the API.
105+
106+ Raises:
107+ ShortlyInvalidLinkError: If the provided link is invalid or malformed.
108+ ShortlyLinkNotFoundError: If the short link does not exist or has expired.
109+ ShortlyTimeoutError: If request exceeds the allowed timeout.
110+ ShortlyConnectionError: If cannot connect to API server.
111+ ShortlyError: For other API-related errors.
112+ """
113+ if silently :
114+ return link
115+
116+ # Shareus.io API configuration
117+ api_url = f"https://api.{ self .base_url } /easy_api"
118+ params = {"key" : self .api_key , "link" : link }
119+
120+ if alias :
121+ params ["alias" ] = alias
122+
123+ try :
124+ async with aiohttp .ClientSession () as session :
125+ headers = {
126+ "User-Agent" : (
127+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
128+ "AppleWebKit/537.36 (KHTML, like Gecko) "
129+ "Chrome/91.0.4472.124 Safari/537.36"
130+ )
131+ }
132+
133+ async with session .get (api_url , params = params , headers = headers , timeout = timeout ) as response :
134+ if response .status != 200 :
135+ raise ShortlyError (f"Failed to shorten your link (HTTP { response .status } )." )
136+
137+ # Shareus returns plain text response
138+ data_text = await response .text ()
139+
140+ if data_text == "settings not saved" :
141+ raise ShortlyError ("Settings not saved or invalid API key." )
142+
143+ # Check if response looks like an error
144+ if any (error_indicator in data_text .lower () for error_indicator in ["error" , "invalid" , "not found" , "failed" ]):
145+ if "invalid" in data_text .lower ():
146+ raise ShortlyInvalidLinkError (data_text )
147+ elif "not found" in data_text .lower () or "expired" in data_text .lower ():
148+ raise ShortlyLinkNotFoundError (data_text )
149+ else :
150+ raise ShortlyError (data_text )
151+
152+ # If we get here, it's likely a successful shortening
153+ return data_text
154+
155+ except asyncio .TimeoutError :
156+ raise ShortlyTimeoutError (f"Request timed out after { timeout } seconds." )
157+ except aiohttp .ClientConnectionError :
158+ raise ShortlyConnectionError (f"Failed to connect to { self .base_url } ." )
159+ except json .JSONDecodeError :
160+ # This is expected for Shareus as it returns plain text, not JSON
161+ raise ShortlyJsonDecodeError (f"Json Error: { e } " )
162+ except Exception as e :
163+ raise ShortlyError (f"An unexpected error occurred: { e } " )
164+
165+ async def tinyurl_convert (self , link , alias = None , silently = False , timeout = 10 ):
166+ """
167+ Shorten a URL using TinyURL API - supports both old (no token) and new (with token) APIs
168+ """
169+ if silently :
170+ return link
171+
172+ # Check if we have a token to decide which API to use
173+ if hasattr (self , 'api_key' ) and self .api_key :
174+ return await _tinyurl_new_api (self , link , alias , timeout )
175+ else :
176+ return await _tinyurl_old_api (self , link , alias , timeout )
177+
178+ async def _tinyurl_old_api (self , link , alias = None , timeout = 10 ):
179+ """
180+ Old TinyURL API (no token required)
181+ Docs: http://tinyurl.com/api-create.php
182+ """
183+ api_url = f"http://{ self .base_url } /api-create.php"
184+ params = {"url" : link }
185+
186+ if alias :
187+ params ["alias" ] = alias
188+
189+ try :
190+ async with aiohttp .ClientSession () as session :
191+ async with session .get (api_url , params = params , timeout = timeout ) as response :
192+ if response .status != 200 :
193+ raise ShortlyError (f"Failed to shorten link (TinyURL error: { response .status } )." )
194+
195+ shortened_url = await response .text ()
196+
197+ if "Error:" in shortened_url :
198+ raise ShortlyError (f"TinyURL error: { shortened_url } " )
199+
200+ return shortened_url .strip ()
201+
202+ except asyncio .TimeoutError :
203+ raise ShortlyTimeoutError (f"TinyURL request timed out after { timeout } seconds." )
204+ except aiohttp .ClientConnectionError :
205+ raise ShortlyConnectionError ("Failed to connect to TinyURL." )
206+ except Exception as e :
207+ raise ShortlyError (f"TinyURL unexpected error: { e } " )
208+
209+ async def _tinyurl_new_api (self , link , alias = None , timeout = 10 ):
210+ """
211+ New TinyURL API (requires token)
212+ Docs: https://tinyurl.com/app/dev/api
213+ """
214+
215+
216+ api_url = f"https://api.{ self .base_url } /create"
217+ headers = {
218+ "Authorization" : f"Bearer { self .api_key } " ,
219+ "Content-Type" : "application/json"
220+ }
221+
222+ payload = {
223+ "url" : link ,
224+ "domain" : "tinyurl.com"
225+ }
226+
227+ if alias :
228+ payload ["alias" ] = alias
229+
230+ try :
231+ async with aiohttp .ClientSession () as session :
232+ async with session .post (
233+ api_url ,
234+ json = payload ,
235+ headers = headers ,
236+ timeout = timeout
237+ ) as response :
238+
239+ # 🔹 Safe JSON parsing with better error handling
240+ try :
241+ response_data = await response .json (content_type = None )
242+ except Exception :
243+ response_text = await response .text ()
244+ response_data = {"raw" : response_text }
245+
246+ # Handle case where alias already exists (returns string)
247+ if "alias already exists" in response_text .lower ():
248+ raise ShortlyError ("Custom alias already exists. Please choose a different one." )
249+
250+ if response .status == 200 and "data" in response_data :
251+ return response_data ["data" ]["tiny_url" ]
252+
253+ elif response .status == 401 :
254+ raise ShortlyError ("Invalid TinyURL API token." )
255+
256+ elif response .status == 422 :
257+ error_msg = "Unknown validation error"
258+ if isinstance (response_data , dict ):
259+ errors = response_data .get ("errors" )
260+ if isinstance (errors , list ) and errors :
261+ error_msg = errors [0 ].get ("message" , error_msg )
262+ elif isinstance (response_data , str ):
263+ error_msg = response_data
264+ raise ShortlyError (f"TinyURL validation error: { error_msg } " )
265+
266+ elif response .status == 429 :
267+ raise ShortlyError ("TinyURL rate limit exceeded. Please try again later." )
268+
269+ else :
270+ # Handle string responses gracefully
271+ if isinstance (response_data , str ):
272+ error_message = response_data
273+ elif isinstance (response_data , dict ) and "raw" in response_data :
274+ error_message = response_data ["raw" ]
275+ else :
276+ error_message = str (response_data )
277+
278+ raise ShortlyError (f"TinyURL API error: { response .status } - { error_message } " )
279+
280+ except asyncio .TimeoutError :
281+ raise ShortlyTimeoutError (f"TinyURL request timed out after { timeout } seconds." )
282+ except aiohttp .ClientConnectionError :
283+ raise ShortlyConnectionError ("Failed to connect to TinyURL API." )
284+ except Exception as e :
285+ raise ShortlyError (f"TinyURL unexpected error: { e } " )
286+
287+ async def bitly_convert (self , link , alias = None , silently = False , timeout = 10 ):
288+ """
289+ Bitly API का उपयोग करके URL को छोटा (shorten) करें।
290+ Docs: https://dev.bitly.com/api-reference
291+ """
292+ if silently :
293+ return link
294+
295+ api_url = f"https://api-ssl.{ self .base_url } /v4/shorten"
296+ headers = {"Authorization" : f"Bearer { self .api_key } " , "Content-Type" : "application/json" }
297+ payload = {"long_url" : link }
298+
299+ # Correct way to add custom alias
300+ if alias :
301+ payload ["custom_bitlink" ] = alias
302+
303+ try :
304+ async with aiohttp .ClientSession () as session :
305+ async with session .post (api_url , headers = headers , json = payload , timeout = timeout ) as response :
306+
307+ # Different error cases handle करें
308+ if response .status == 401 :
309+ raise ShortlyError ("Invalid Bitly API key" )
310+ elif response .status == 400 :
311+ error_text = await response .text ()
312+ if "CUSTOM_BITLINK_ALREADY_EXISTS" in error_text :
313+ raise ShortlyError (f"Custom alias '{ alias } ' already exists on Bitly" )
314+ elif "INVALID_CUSTOM_BITLINK" in error_text :
315+ raise ShortlyError (f"Invalid custom alias: { alias } " )
316+ else :
317+ raise ShortlyError ("Invalid URL provided to Bitly" )
318+ elif response .status == 403 :
319+ raise ShortlyError ("Bitly API permission denied" )
320+ elif response .status == 409 :
321+ raise ShortlyError ("Custom alias already exists" )
322+ elif response .status != 200 and response .status != 201 :
323+ raise ShortlyError (f"Bitly error: { await response .text ()} " )
324+
325+ try :
326+ data = await response .json ()
327+ except Exception as e :
328+ raise ShortlyJsonDecodeError (f"Invalid JSON from Bitly: { e } " )
329+
330+ return data .get ("link" )
331+
332+ except asyncio .TimeoutError :
333+ raise ShortlyTimeoutError (f"Bitly request timed out after { timeout } seconds." )
334+ except aiohttp .ClientConnectionError :
335+ raise ShortlyConnectionError ("Failed to connect to Bitly." )
336+ except Exception as e :
337+ raise ShortlyError (f"Bitly unexpected error: { e } " )
0 commit comments