def authenticate(self, **kwargs): if 'username' in kwargs: return None # validate the access token with /me endpoint and get user information client = Client(access_token=kwargs.get("access_token")) user = client.me() if 'name' not in user: return None user_model = get_user_model() metadata = {} profile_about, profile_image, profile_location = None, None, None if 'json_metadata' in user["account"]: metadata = json.loads(user["account"]["json_metadata"]) profile = metadata.get("profile", {}) profile_about = profile.get("about") profile_image = profile.get("profile_image") profile_location = profile.get("location") try: user = user_model.objects.get(username=user["name"]) except user_model.DoesNotExist: user = user_model.objects.create_user(username=user["name"], name=user["name"]) user.profile_about = profile_about user.profile_image = profile_image user.profile_location = profile_location user.save() return user
def test_get_login_url(self): c = Client("client_id" "client_secret") self.assertEqual( c.get_login_url("http://localhost", "login"), "https://v2.steemconnect.com/oauth2/authorize?client_id" "=client_idclient_secret&redirect_uri=http%3A%2F%2Flocalh" "ost&scope=login")
def test_get_login_url_override_defaults(self): c = Client(oauth_base_url="http://foo.bar/oauth2/", client_id="client_id", client_secret="client_secret") self.assertEqual( c.get_login_url("http://localhost", "login"), "http://foo.bar/oauth2/authorize?client_id=client_id&" "redirect_uri=http%3A%2F%2Flocalhost&scope=login")
def login(): sc_client = ScClient(client_id=SC_CLIENT_ID, client_secret=SC_SECRET, oauth_base_url="https://hivesigner.com/oauth2/", sc2_api_base_url="https://hivesigner.com/api/") login_url = sc_client.get_login_url( f"{SITE_URL}/gift-codes", "login", ) return render_template("login.html", login_url=login_url)
def login(): c = Client( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, ) auth_url = c.get_login_url( "http://127.0.0.1:5000/complete/steemconnect/", "login,vote,comment,custom_json", ) return redirect(auth_url)
def steemconnect(self, accesstoken=None): if self.sc: return self.sc if accesstoken: self.accesstoken = accesstoken if self.accesstoken: self.sc = Client(access_token=self.accesstoken) else: self.sc = Client(client_id=self.client_id, client_secret=self.client_secret) return self.sc
def wrapper (self, *args, **kwargs) : username = '' access_token = '' request = self.request if request.method == 'GET' or request.method == 'DELETE': if 'username' in request.GET : username = request.GET['username'] if 'access_token' in request.GET : access_token = request.GET['access_token'] else : if 'username' in request.data : username = request.data['username'] if 'access_token' in request.data : access_token = request.data['access_token'] if username == '' or access_token == '' : raise PermissionDenied token = AccessToken.objects.filter(username = username) if len(token) == 0 or token[0].token != sha512(access_token) : # Validate access token with Steemconnect c = Client(access_token = access_token) try: user = c.me() except: return HttpResponse(status=503) if user['user'] != username : # Wrong username / access_token pair raise PermissionDenied if len(token) == 0 : token = AccessToken(username = username, token = sha512(access_token)) token.save() else : # Access token was updated token.update(token = sha512(access_token)) # User is valid, do we need to create a new SteemUser object? users = SteemUser.objects.filter(username = username) if len(users) == 0 : user = SteemUser(username = username) user.save() else : user = users[0] kwargs['user'] = user return func(self, *args, **kwargs)
def test_hot_sign(self): c = Client() sign_url = c.hot_sign("transfer", { "to": "turbot", "amount": "0.001 SBD", "memo": "selam", }, redirect_uri="http://localhost") self.assertEqual( sign_url, "https://v2.steemconnect.com/sign/tra" "nsfer?to=turbot&amount=0.001+SBD&me" "mo=selam&redirect_uri=http%3A%2F%2F" "localhost")
def test_me(self): def request_callback(request): self.assertEqual(request.url, "https://v2.steemconnect.com/api/me/") return 200, {}, json.dumps({"access_token": "foo"}) c = Client(access_token="foo") responses.add_callback( responses.POST, 'https://v2.steemconnect.com/api/me/', callback=request_callback, ) c.me()
def update_access_token(self, secret): "This function should be used if access_token expires." tokens = Client.get_refresh_token(code=self.code, app_secret=secret) access_token = tokens["access_token"] SteemConnectUser.objects.filter(user=self.user).update( access_token=access_token) return access_token
def test_hot_sign_without_redirect_uri(self): c = Client() sign_url = c.hot_sign( "transfer", { "to": "turbot", "amount": "0.001 SBD", "memo": "selam", }, ) self.assertEqual( sign_url, "https://v2.steemconnect.com/sign/tra" "nsfer?to=turbot&amount=0.001+SBD&me" "mo=selam")
def steemconnect(self, accesstoken=None): ''' Initializes the SteemConnect Client class ''' if self.sc is not None: return self.sc if accesstoken is not None: self.accesstoken = accesstoken if self.accesstoken is None: self.sc = Client(client_id=self.client_id, client_secret=self.client_secret) else: self.sc = Client(access_token=self.accesstoken, client_id=self.client_id, client_secret=self.client_secret) return self.sc
def get_sc_client(): global _sc_client if not _sc_client: _sc_client = Client(client_id=settings.SC_CLIENT_ID, client_secret=settings.SC_CLIENT_SECRET) return _sc_client
class LoginSignup(View): client = Client(client_id=settings.CLIENT_ID, redirect_url=settings.REDIRECT_URL, code=settings.CODE, scope=settings.SCOPE) def get(self, request, *args, **kwargs): code = request.GET["code"] tokens = self.client.get_refresh_token(code, settings.APP_SECRET) username = tokens["username"] access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] user, created = User.objects.get_or_create(username=username) if SteemConnectUser.objects.filter(user=user).exists(): SteemConnectUser.objects.filter(user=user).update( code=code, access_token=access_token, refresh_token=refresh_token) else: SteemConnectUser(user=user, code=code, access_token=access_token, refresh_token=refresh_token).save() login(request, user, backend="django.contrib.auth.backends.ModelBackend") return HttpResponseRedirect(settings.LOGIN_REDIRECT)
def gift_codes(): sc_client = ScClient(access_token=request.args.get("access_token"), oauth_base_url="https://hivesigner.com/oauth2/", sc2_api_base_url="https://hivesigner.com/api/") me = sc_client.me() if 'error' in me: return "Invalid access token" # if the user already claimed their gift codes, # then there is no need to create new gift codes if g.infestor.gift_code_manager.get_gift_code_count_by_user( me["account"]["name"]) > 0: gift_codes = g.infestor.gift_code_manager.get_gift_codes_by_user( me["account"]["name"]) return render_template("gift_codes.html", user=me, gift_codes=gift_codes) # quick hack # no need to fill all account data into Lighthive.helpers.Account # since all we're interested in is reputation figure. acc = Account(client=g.lightsteem_client) acc.raw_data = {"reputation": me["account"]["reputation"]} gift_code_count = 0 if acc.reputation() < g.minimum_rep: error = "Your reputation is not enough to claim a free account." return render_template("gift_codes.html", error=error) else: gift_code_count += 1 # check if the account is eligible for the bonus if OPERATOR_WITNESS in me["account"]["witness_votes"]: gift_code_count += 1 # create gift_codes based on the *gift_code_count* for i in range(gift_code_count): code = random.randint(1000000, 999999999) g.infestor.gift_code_manager.add_code( code, created_for=me["account"]["name"]) gift_codes = g.infestor.gift_code_manager.get_gift_codes_by_user( me["account"]["name"]) return render_template("gift_codes.html", user=me, gift_codes=gift_codes, error=None)
def create_post(): data_dict = json.loads(request.data) if 'user_id' not in data_dict: raise BadRequest('user_id is mandatory param') if 'title' not in data_dict: raise BadRequest('title is mandatory param') if 'body' not in data_dict: raise BadRequest('body is mandatory param') user_id = data_dict['user_id'] title = data_dict['title'] body = data_dict['body'] force_permlink = data_dict[ 'force_permlink'] if 'force_permlink' in data_dict else '' cover_image_url = data_dict.get('cover_image_url', '') user = User.query.filter_by(user_id=user_id).first() if not user: raise Unauthorized('Not authorized with steemconnect') client = Client(access_token=user.steem_token) permlink = force_permlink or title.lower().replace(' ', '-')\ .replace('_', '-')\ .replace(')', '-')\ .replace('(', '-')\ .encode('ascii', 'ignore') if not permlink or len(permlink) < 4: permlink = str(uuid4()) comment = Comment( user.user_id, permlink, "Make donations/tipping easy <a href=\"http://donatenow.io\">donatenow!</a>", title=title, json_metadata={ "app": app_name, "body": body, "cover_image_url": cover_image_url }) r = client.broadcast([comment.to_operation_structure()]) if 'error_description' in r and r['error_description']: raise BadRequest(r['error_description']) all_posts = get_all(user_id) post = all_posts['posts'][permlink] response = Response(json.dumps(post), mimetype='application/json') response.headers.add('Access-Control-Allow-Origin', '*') return response
def test_refresh_access_token(self): def request_callback(request): self.assertEqual(request.url, "https://v2.steemconnect.com/api/oauth2/token/") self.assertEqual( request.body, 'refresh_token=refresh_token&client_id=client_id&' 'client_secret=client_secret&scope=login') return 200, {}, json.dumps({"access_token": "foo"}) c = Client(client_id="client_id", client_secret="client_secret") responses.add_callback( responses.POST, 'https://v2.steemconnect.com/api/oauth2/token/', callback=request_callback, ) c.refresh_access_token("refresh_token", "login")
def test_get_access_token(self): def request_callback(request): self.assertEqual(request.url, "https://v2.steemconnect.com/api/oauth2/token/") self.assertEqual( request.body, 'grant_type=authorization_code&code=code&client_id=' 'client_id&client_secret=client_secret') return 200, {}, json.dumps({"access_token": "foo"}) c = Client(client_id="client_id", client_secret="client_secret") responses.add_callback( responses.POST, 'https://v2.steemconnect.com/api/oauth2/token/', callback=request_callback, ) c.get_access_token("code")
def form_valid(self, form): post = form.save(commit=False) post.author = self.request.user tags = self.request.POST.get('tags') tags_list = tags.split(',') post.status = 'submitted' post.save() form.save_m2m() messages.success(self.request, 'Post Submitted and under review') if not self.request.user.username == 'admin': # remember to remove in production user = self.request.user else: user = User.objects.get(username='******') profile = Profile.objects.get(user=user) posting_key = profile.posting_key refresh_token = profile.refresh_token url = "https://v2.steemconnect.com/api/oauth2/token" response_access = requests.post(url, data={ 'refresh_token': refresh_token, 'client_id': 'sportherald.app', 'client_secret': settings.CLIENT_SECRET, 'scope': "vote,comment,offline" }) access_token = response_access.json().get('access_token') c = Client(access_token=access_token) comment = Comment(author=user.username, permlink=post.slug, body=post.body, title=post.title, parent_permlink="sportherald", json_metadata={ "app": "sportherlad.app", 'tags': tags_list }) c.broadcast([comment.to_operation_structure()]) #return JsonResponse({'status': 200, 'slug': post.slug, 'posting_key': posting_key, 'username': user.username}) return HttpResponseRedirect('/')
def get_sc_client(): global _sc_client if not _sc_client: _sc_client = Client( client_id=settings.SC_CLIENT_ID, client_secret=settings.SC_CLIENT_SECRET, oauth_base_url="https://hivesigner.com/oauth2/", sc2_api_base_url="https://hivesigner.com/api/", ) return _sc_client
def authenticate(self, request, **kwargs): if 'username' in kwargs: return None # validate the access token with /me endpoint and get user information client = Client(access_token=kwargs.get("access_token")) user = client.me() if 'name' not in user: return None user_model = get_user_model() try: user_instance = user_model.objects.get(username=user["name"]) user_instance.save() except user_model.DoesNotExist: user_instance = user_model.objects.create_user( username=user["name"]) return user_instance
from flask import Flask, request from steemconnect.client import Client app = Flask(__name__) client_id = "your.app" client_secret = "your_secret" c = Client(client_id=client_id, client_secret=client_secret) @app.route('/') def index(): login_url = c.get_login_url( "http://*****:*****@app.route('/welcome') def welcome(): c.access_token = request.args.get("access_token") return "Welcome <strong>%s</strong>!" % c.me()["name"]
def get_client(request): community_model = request.community_model return Client(client_id=community_model.client_id, redirect_url=community_model.redirect_url, code=True, scope=community_model.scope)
def vote(request, user, permlink): if request.method != "POST": raise Http404 # django admin users should not be able to vote. if not request.session.get("sc_token"): redirect('logout') try: poll = Question.objects.get(username=user, permlink=permlink) except Question.DoesNotExist: raise Http404 if not request.user.is_authenticated: return redirect('login') choice_id = request.POST.get("choice-id") additional_thoughts = request.POST.get("vote-comment", "") if not choice_id: raise Http404 if Choice.objects.filter(voted_users__username=request.user, question=poll).exists(): messages.add_message(request, messages.ERROR, "You have already voted for this poll!") return redirect("detail", poll.username, poll.permlink) if not poll.is_votable(): messages.add_message(request, messages.ERROR, "This poll is expired!") return redirect("detail", poll.username, poll.permlink) try: choice = Choice.objects.get(pk=int(choice_id)) except Choice.DoesNotExist: raise Http404 choice.voted_users.add(request.user) # send it to the steem blockchain sc_client = Client(access_token=request.session.get("sc_token")) choice_text = choice.text.strip() body = f"Voted for *{choice_text}*." if additional_thoughts: body += f"\n\n{additional_thoughts}" comment = Comment(author=request.user.username, permlink=str(uuid.uuid4()), body=body, parent_author=poll.username, parent_permlink=poll.permlink, json_metadata={ "tags": settings.DEFAULT_TAGS, "app": f"dpoll/{settings.DPOLL_APP_VERSION}", "content_type": "poll_vote", "vote": choice.text }) comment_options = get_comment_options(comment) resp = sc_client.broadcast([ comment.to_operation_structure(), comment_options.to_operation_structure(), ]) if 'error' in resp: messages.add_message(request, messages.ERROR, resp.get("error_description", "error")) choice.voted_users.remove(request.user) return redirect("detail", poll.username, poll.permlink) messages.add_message(request, messages.SUCCESS, "You have successfully voted!") return redirect("detail", poll.username, poll.permlink)
def create_poll(request): if not request.user.is_authenticated: return redirect('login') error = False # @todo: create a form class for that. this is very ugly. if request.method == 'POST': if not 'sc_token' in request.session: return redirect("/") required_fields = ["question", "answers[]", "expire-at"] for field in required_fields: if not request.POST.get(field): error = True messages.add_message(request, messages.ERROR, f"{field} field is required.") question = request.POST.get("question") choices = request.POST.getlist("answers[]") expire_at = request.POST.get("expire-at") if question: if not (4 < len(question) < 256): messages.add_message( request, messages.ERROR, "Question text should be between 6-256 chars.") error = True if 'choices' in request.POST: if len(choices) < 2: messages.add_message(request, messages.ERROR, f"At least 2 choices are required.") error = True elif len(choices) > 20: messages.add_message(request, messages.ERROR, f"Maximum number of choices is 20.") error = True if 'expire-at' in request.POST: if expire_at not in ["1_week", "1_month"]: messages.add_message(request, messages.ERROR, f"Invalid expiration value.") error = True if error: return render(request, "add.html") days = 7 if expire_at == "1_week" else 30 # add the question permlink = slugify(question)[0:256] if not permlink: permlink = str(uuid.uuid4()) # @todo: also check for duplicates in the blockchain. # client.get_content() if (Question.objects.filter(permlink=permlink, username=request.user)).exists(): messages.add_message(request, messages.ERROR, "You have already a similar poll.") return redirect('create-poll') question = Question(text=question, username=request.user.username, description=request.POST.get("description"), permlink=permlink, expire_at=now() + timedelta(days=days)) question.save() # add answers attached to it for choice in choices: choice_instance = Choice( question=question, text=choice, ) choice_instance.save() # send it to the steem blockchain sc_client = Client(access_token=request.session.get("sc_token")) comment = Comment(author=request.user.username, permlink=question.permlink, body=get_body( question, choices, request.user.username, permlink, ), title=question.text, parent_permlink=settings.COMMUNITY_TAG, json_metadata={ "tags": settings.DEFAULT_TAGS, "app": f"dpoll/{settings.DPOLL_APP_VERSION}", "content_type": "poll", "question": question.text, "description": question.description or "", "choices": choices, "expire_at": str(question.expire_at), }) comment_options = get_comment_options(comment) resp = sc_client.broadcast([ comment.to_operation_structure(), comment_options.to_operation_structure(), ]) if 'error' in resp: if 'The token has invalid role' in resp.get("error_description"): # expired token auth_logout(request) return redirect('login') messages.add_message(request, messages.ERROR, resp.get("error_description", "error")) question.delete() return redirect('create-poll') return redirect('detail', question.username, question.permlink) return render(request, "add.html")
def login_complete(): token = request.args['access_token'] c = Client(access_token=token, ) user_info = c.me() create_or_update_user(user_info['name'], token) return redirect(FE_URL.format(user_info['name']))
def create_poll(request): if not request.user.is_authenticated: return redirect('login') if request.method == 'POST': form_data = copy.copy(request.POST) if 'sc_token' not in request.session: return redirect("/") error, question, choices, expire_at, permlink, days, tags, \ allow_multiple_choices = validate_input(request) if error: form_data.update({ "answers": request.POST.getlist("answers[]"), "expire_at": request.POST.get("expire-at"), "reward_option": request.POST.get("reward-option"), "allow_multiple_choices": request.POST.get("allow-multiple-choices"), }) return render(request, "add.html", {"form_data": form_data}) if (Question.objects.filter(permlink=permlink, username=request.user)).exists(): messages.add_message(request, messages.ERROR, "You have already a similar poll.") return redirect('create-poll') # add question question = add_or_get_question(request, question, permlink, days, allow_multiple_choices) question.save() # add answers attached to it add_choices(question, choices) # send it to the steem blockchain sc_client = Client(access_token=request.session.get("sc_token")) comment = get_comment(request, question, choices, permlink, tags) comment_options = get_comment_options( comment, reward_option=request.POST.get("reward-option")) if not settings.BROADCAST_TO_BLOCKCHAIN: resp = {} else: resp = sc_client.broadcast([ comment.to_operation_structure(), comment_options.to_operation_structure(), ]) if 'error' in resp: if 'The token has invalid role' in resp.get("error_description"): # expired token auth_logout(request) return redirect('login') messages.add_message(request, messages.ERROR, resp.get("error_description", "error")) question.delete() return redirect('create-poll') return redirect('detail', question.username, question.permlink) return render(request, "add.html")
from steem.account import Account from steem.amount import Amount from steemconnect.client import Client from steemconnect.operations import Follow, Unfollow, Mute, ClaimRewardBalance, Comment, CommentOptions, Vote import requests, json, os, random, string St_username = "" Tag = '' s = Steem() c = Converter() app = Flask(__name__) assist = Assistant(app, route='/api', project_id=os.environ.get('project_id')) app.config['INTEGRATIONS'] = ['ACTIONS_ON_GOOGLE'] # To enable Rich Messages posts = s.get_discussions_by_trending({"limit": "8" }) # To cache the top 8 trending posts sc = Client(client_id=os.environ.get('client_id'), client_secret=os.environ.get('client_secret')) class Steemian: def __init__(self, St_username): self.username = St_username self.data = Account(self.username) self.reputation = str(self.data.rep) self.upvoteworth = self.calculate_voteworth() self.steempower = self.calculate_steempower(True) self.availablesp = self.calculate_steempower( False) # To get the amount of Steempower that can be delegated self.wallet = self.data.balances self.accountworth = self.calculate_accountworth() self.steemprice = self.cmc_price( '1230') # To get the price of Steem form coinmarketcap
def vote(request, user, permlink): if request.method != "POST": raise Http404 # django admin users should not be able to vote. if not request.session.get("sc_token"): redirect('logout') try: poll = Question.objects.get(username=user, permlink=permlink) except Question.DoesNotExist: raise Http404 if not request.user.is_authenticated: return redirect('login') if poll.allow_multiple_choices: choice_ids = request.POST.getlist("choice-id") else: choice_ids = [ request.POST.get("choice-id"), ] # remove noise choice_ids = [x for x in choice_ids if x is not None] additional_thoughts = request.POST.get("vote-comment", "") if not len(choice_ids): messages.add_message(request, messages.ERROR, "You need to pick a choice to vote.") return redirect("detail", poll.username, poll.permlink) if Choice.objects.filter(voted_users__username=request.user, question=poll).exists(): messages.add_message(request, messages.ERROR, "You have already voted for this poll!") return redirect("detail", poll.username, poll.permlink) if not poll.is_votable(): messages.add_message(request, messages.ERROR, "This poll is expired!") return redirect("detail", poll.username, poll.permlink) for choice_id in choice_ids: try: choice = Choice.objects.get(pk=int(choice_id)) except Choice.DoesNotExist: raise Http404 choice_instances = [] for choice_id in choice_ids: choice = Choice.objects.get(pk=int(choice_id)) choice_instances.append(choice) # send it to the steem blockchain sc_client = Client(access_token=request.session.get("sc_token")) choice_text = "" for c in choice_instances: choice_text += f" - {c.text.strip()}\n" body = f"Voted for \n {choice_text}" if additional_thoughts: body += f"\n\n{additional_thoughts}" comment = Comment(author=request.user.username, permlink=str(uuid.uuid4()), body=body, parent_author=poll.username, parent_permlink=poll.permlink, json_metadata={ "tags": settings.DEFAULT_TAGS, "app": f"dpoll/{settings.DPOLL_APP_VERSION}", "content_type": "poll_vote", "votes": [c.text.strip() for c in choice_instances], }) comment_options = get_comment_options(comment) if not settings.BROADCAST_TO_BLOCKCHAIN: resp = {} else: resp = sc_client.broadcast([ comment.to_operation_structure(), comment_options.to_operation_structure(), ]) # Steemconnect sometimes returns 503. # https://github.com/steemscript/steemconnect/issues/356 if not isinstance(resp, dict): messages.add_message( request, messages.ERROR, "We got an unexpected error from Steemconnect. Please, try again.") return redirect("detail", poll.username, poll.permlink) # Expected way to receive errors on broadcasting if 'error' in resp: messages.add_message(request, messages.ERROR, resp.get("error_description", "error")) return redirect("detail", poll.username, poll.permlink) # register the vote to the database for choice_instance in choice_instances: choice_instance.voted_users.add(request.user) block_id = resp.get("result", {}).get("block_num") trx_id = resp.get("result", {}).get("id") # add trx id and block id to the audit log vote_audit = VoteAudit(question=poll, voter=request.user, block_id=block_id, trx_id=trx_id) vote_audit.save() for choice_instance in choice_instances: vote_audit.choices.add(choice_instance) messages.add_message(request, messages.SUCCESS, "You have successfully voted!") return redirect("detail", poll.username, poll.permlink)
def edit_poll(request, author, permlink): if not request.user.is_authenticated: return redirect('login') try: poll = Question.objects.get( permlink=permlink, username=author, ) except Question.DoesNotExist: raise Http404 if author != request.user.username: raise Http404 if request.method == "GET": poll_data = fetch_poll_data(poll.username, poll.permlink) tags = poll_data.get("tags", []) tags = [tag for tag in tags if tag not in settings.DEFAULT_TAGS] form_data = { "question": poll.text, "description": poll.description, "answers": [c.text for c in Choice.objects.filter(question=poll)], "expire_at": poll.expire_at_humanized, "tags": ",".join(tags), "allow_multiple_choices": poll.allow_multiple_choices } if request.method == 'POST': form_data = copy.copy(request.POST) if 'sc_token' not in request.session: return redirect("/") error, question, choices, expire_at, _, days, tags, \ allow_multiple_choices = validate_input(request) if tags: tags = settings.DEFAULT_TAGS + tags else: tags = settings.DEFAULT_TAGS permlink = poll.permlink if error: form_data.update({ "answers": request.POST.getlist("answers[]"), "expire_at": request.POST.get("expire-at"), "allow_multiple_choices": request.POST.get("allow-multiple-choices"), }) return render(request, "edit.html", {"form_data": form_data}) # add question question = add_or_get_question(request, question, permlink, days, allow_multiple_choices) question.save() # add answers attached to it add_choices(question, choices, flush=True) # send it to the steem blockchain sc_client = Client(access_token=request.session.get("sc_token")) comment = get_comment(request, question, choices, permlink, tags=tags) if not settings.BROADCAST_TO_BLOCKCHAIN: resp = {} else: resp = sc_client.broadcast([ comment.to_operation_structure(), ]) if 'error' in resp: if 'The token has invalid role' in resp.get("error_description"): # expired token auth_logout(request) return redirect('login') messages.add_message(request, messages.ERROR, resp.get("error_description", "error")) question.delete() return redirect('edit', args=(author, permlink)) return redirect('detail', question.username, question.permlink) return render(request, "edit.html", { "form_data": form_data, })