I'm using TFS 2017 update 1 on premises. I'm using #ID in log comments of commits in order to associate workitem ID (of User Story, Task etc.) with GIT commits of source code. It properly works (I can see links to commit from workitem interface).
I'd like to use TFS SDK API with tfs aggregator in order to better manage GIT commits (e.g. automatic transition to custom state of workitem when a specific custom git commit message is done by programmers).
How can access message/log of git commits from Microsoft.TeamFoundation.WorkItemTracking.Client in order be able to parser custom message in addition to those described here (e.g. "Fixes #123" or "Close #123")?
You can't get the commit comment only with WorkItemHttpClient, you can get it along with GitHttpClient. first of all get the work item links with WorkItemHttpClient, than get the commit id and get the comment with GitHttpClient.
A working example:
VssClientCredentials cred = new VssClientCredentials();
VssConnection tfs = new VssConnection(new Uri("http://tfs-server:8080/tfs/collection"), cred);
var workItemClient = tfs.GetClient<WorkItemTrackingHttpClient>();
var gitClient = tfs.GetClient<GitHttpClient>();
int workItemId = 1213;
var workItem = workItemClient.GetWorkItemAsync("Project-Name", workItemId, expand: WorkItemExpand.Relations).Result;
// We need to retrieve the commit id from the links, debug the following line to understand what I did
var commitId = wit.Relations.Where(r => r.Url.Contains("Git")).FirstOrDefault().Url.Split('%')[2].Remove(0,2);
var commit = gitClient.GetCommitAsync("Project-Name", commitId, "Repo-Name").Result;
string comment = commit.comment;
By the way, you can't use the Fixes #123 syntax because is not supported in TFS 2017.
I am retrieving some User Stories using the the VSTS / TFS Web API and the code below:
var getWorkItemsHttpRequestMessage = new HttpRequestMessage(new HttpMethod("GET"), uri + "/_apis/wit/workitems?ids=736,731&&api-version=4.1");
var getWorkItemsHttpResponse = client.SendAsync(getWorkItemsHttpRequestMessage).Result;
if (getWorkItemsHttpResponse.IsSuccessStatusCode)
{
var workItems = getWorkItemsHttpResponse.Content.ReadAsAsync<HttpWorkItems>().Result;
// ...
The query returns all the fields of the work items (user stories, in this case), but not the other items that is linked to them.
I would like to retrieve the Tasks related to these User Stories.
How can it be done ?
Is there another better way to do it ?
You can use the $expand parameter in the URL with the value relations:
https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/4?$expand=relations&api-version=4.1
In the results, you will get the work item links in the section relations:
"relations":[
{
"rel":"System.LinkTypes.Hierarchy-Forward",
"url":"https://dev.azure.com/shaykia/_apis/wit/workItems/5",
"attributes":{
"isLocked":false
}
In the example above, we check work item 4 in the API, and in the results, we can see that work item 5 linked to him with a type of System.LinkTypes.Hierarchy-Forward that work item 4 the parent of 5 (5 he is the child, a task in this case).
You can read here about the relations types.
I'm using TFS 2017 update 1 on premises. I'm using #ID in log comments of commits in order to associate workitem ID (of User Story, Task etc.) with GIT commits of source code. It properly works (I can see links to commit from workitem interface) but I'd like to use TFS SDK API with tfs aggregator in order to better manage GIT commits (e.g. dashboards using custom fields) . How can access git commits from Microsoft.TeamFoundation.WorkItemTracking.Client ?
The git commits are linked to workitems with "ExternalLink" type. So you can get the links of the work item to query that information.
WorkItemStore wis = ttpc.GetService<WorkItemStore>();
int workitemid = 1;
WorkItem wi = wis.GetWorkItem(workitemid);
foreach (Link w in wi.Links)
{
if (w.GetType().ToString() == "Microsoft.TeamFoundation.WorkItemTracking.Client.ExternalLink")
{
ExternalLink el = (ExternalLink)w;
Console.WriteLine(el.LinkedArtifactUri);
}
}
The LinkedArtifactUri will include the ID of the git commit. And then you can get the commit information via:
GitHttpClient ght = ttpc.GetClient<GitHttpClient>();
GitCommit gc = ght.GetCommitAsync("CommitID", "RepoID",99).Result;
Console.WriteLine(gc.Committer.Date);
Console.WriteLine(gc.Committer.Name);
foreach (GitChange gch in gc.Changes)
{
Console.WriteLine(gch.Item.Path);
}
Console.ReadLine();
In the Instructor Dashboard of our OpenEdX, the Mail tab is missing. This allows bulk mailing to OpenEdx participants.
How to enable the Instructor Dashboard > Mail functionality ?
Edit: I though is would be a parameter in /lms/envs/common.py but it doesn't seems to be there. I noticed the test ./lms/djangoapps/bulk_email/tests/test_email.py#L198 with :
#patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
But I didnt find any meaningful instances of it elsewhere.
The way you [configure bulk email has changed] in 2016. As of 2018, see 6.2. Changing the Bulk Email Setting : http://edx.readthedocs.io/projects/open-edx-release-notes/en/latest/bulk_email.html
On http://{your_URL}/admin > Bulk email flags > Add > Set it such :
And then :
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 8 months ago.
The community reviewed whether to reopen this question 7 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
We currently have a Slack channel with ~8K messages all comes from Jenkins integration. Is there any programmatic way to delete all messages from that channel? The web interface can only delete 100 messages at a time.
I quickly found out there's someone already made a helper: slack-cleaner for this.
And for me it's just:
slack-cleaner --token=<TOKEN> --message --channel jenkins --user "*" --perform
I wrote a simple node script for deleting messages from public/private channels and chats. You can modify and use it.
https://gist.github.com/firatkucuk/ee898bc919021da621689f5e47e7abac
First, modify your token in the scripts configuration section then run the script:
node ./delete-slack-messages CHANNEL_ID
Get an OAuth token:
Go to https://api.slack.com/apps
Click 'Create New App', and name your (temporary) app.
In the side nav, go to 'Oauth & Permissions'
On that page, find the 'Scopes' section. Click 'Add an OAuth Scope' and add 'channels:history' and 'chat:write'. (see scopes)
At the top of the page, Click 'Install App to Workspace'. Confirm, and on page reload, copy the OAuth Access Token.
Find the channel ID
Also, the channel ID can be seen in the browser URL when you open slack in the browser. e.g.
https://mycompany.slack.com/messages/MY_CHANNEL_ID/
or
https://app.slack.com/client/WORKSPACE_ID/MY_CHANNEL_ID
default clean command did not work for me giving following error:
$ slack-cleaner --token=<TOKEN> --message --channel <CHANNEL>
Running slack-cleaner v0.2.4
Channel, direct message or private group not found
but following worked without any issue to clean the bot messages
slack-cleaner --token <TOKEN> --message --group <CHANNEL> --bot --perform --rate 1
or
slack-cleaner --token <TOKEN> --message --group <CHANNEL> --user "*" --perform --rate 1
to clean all the messages.
I use rate-limit of 1 second to avoid HTTP 429 Too Many Requests error because of slack api rate limit. In both cases, channel name was supplied without # sign
For anyone else who doesn't need to do it programmatic,
here's a quick way:
(probably for paid users only)
Open the channel in web or the desktop app, and click the cog (top right).
Choose "Additional options..." to bring up the archival menu. notes
Select "Set the channel message retention policy".
Set "Retain all messages for a specific number of days".
All messages older than this time are deleted permanently!
I usually set this option to "1 day" to leave the channel with some context, then I go back into the above settings, and set it's retention policy back to "default" to go continue storing them from now-on.
Notes:
Luke points out: If the option is hidden: you have to go to global workspace Admin settings, Message Retention & Deletion, and check "Let workspace members override these settings"
!!UPDATE!!
as #niels-van-reijmersdal metioned in comment.
This feature has been removed. See this thread for more info: twitter.com/slackhq/status/467182697979588608?lang=en
!!END UPDATE!!
Here is a nice answer from SlackHQ in twitter, and it works without any third party stuff.
https://twitter.com/slackhq/status/467182697979588608?lang=en
You can bulk delete via the archives (http://my.slack.com/archives )
page for a particular channel: look for "delete messages" in menu
Option 1 You can set a Slack channel to automatically delete messages after 1 day, but it's a little hidden. First, you have to go to your Slack Workspace Settings, Message Retention & Deletion, and check "Let workspace members override these settings". After that, in the Slack client you can open a channel, click the gear, and click "Edit message retention..."
Option 2 The slack-cleaner command line tool that others have mentioned.
Option 3 Below is a little Python script that I use to clear Private channels. Can be a good starting point if you want more programmatic control of deletion. Unfortunately Slack has no bulk-delete API, and they rate-limit the individual delete to 50 per minute, so it unavoidably takes a long time.
# -*- coding: utf-8 -*-
"""
Requirement: pip install slackclient
"""
import multiprocessing.dummy, ctypes, time, traceback, datetime
from slackclient import SlackClient
legacy_token = raw_input("Enter token of an admin user. Get it from https://api.slack.com/custom-integrations/legacy-tokens >> ")
slack_client = SlackClient(legacy_token)
name_to_id = dict()
res = slack_client.api_call(
"groups.list", # groups are private channels, conversations are public channels. Different API.
exclude_members=True,
)
print ("Private channels:")
for c in res['groups']:
print(c['name'])
name_to_id[c['name']] = c['id']
channel = raw_input("Enter channel name to clear >> ").strip("#")
channel_id = name_to_id[channel]
pool=multiprocessing.dummy.Pool(4) #slack rate-limits the API, so not much benefit to more threads.
count = multiprocessing.dummy.Value(ctypes.c_int,0)
def _delete_message(message):
try:
success = False
while not success:
res= slack_client.api_call(
"chat.delete",
channel=channel_id,
ts=message['ts']
)
success = res['ok']
if not success:
if res.get('error')=='ratelimited':
# print res
time.sleep(float(res['headers']['Retry-After']))
else:
raise Exception("got error: %s"%(str(res.get('error'))))
count.value += 1
if count.value % 50==0:
print(count.value)
except:
traceback.print_exc()
retries = 3
hours_in_past = int(raw_input("How many hours in the past should messages be kept? Enter 0 to delete them all. >> "))
latest_timestamp = ((datetime.datetime.utcnow()-datetime.timedelta(hours=hours_in_past)) - datetime.datetime(1970,1,1)).total_seconds()
print("deleting messages...")
while retries > 0:
#see https://api.slack.com/methods/conversations.history
res = slack_client.api_call(
"groups.history",
channel=channel_id,
count=1000,
latest=latest_timestamp,)#important to do paging. Otherwise Slack returns a lot of already-deleted messages.
if res['messages']:
latest_timestamp = min(float(m['ts']) for m in res['messages'])
print datetime.datetime.utcfromtimestamp(float(latest_timestamp)).strftime("%r %d-%b-%Y")
pool.map(_delete_message, res['messages'])
if not res["has_more"]: #Slack API seems to lie about this sometimes
print ("No data. Sleeping...")
time.sleep(1.0)
retries -= 1
else:
retries=10
print("Done.")
Note, that script will need modification to list & clear public channels. The API methods for those are channels.* instead of groups.*
As other answers allude, Slack's rate limits make this tricky - the rate limit is relatively low for their chat.delete API at ~50 requests per minute.
The best strategy that respects the rate limit is to retrieve messages from the channel you want to clear, then delete the messages in batches under 50 that run on a minutely interval.
I've built a project containing an example of this batching that you can easily fork and deploy on Autocode - it lets you clear a channel via slash command (and allows you restrict access to the command to just certain users of course!). When you run /cmd clear in a channel, it marks that channel for clearing and runs the following code every minute until it deletes all the messages in the channel:
console.log(`About to clear ${messages.length} messages from #${channel.name}...`);
let deletionResults = await async.mapLimit(messages, 2, async (message) => {
try {
await lib.slack.messages['#0.6.1'].destroy({
id: clearedChannelId,
ts: message.ts,
as_user: true
});
return {
successful: true
};
} catch (e) {
return {
successful: false,
retryable: e.message && e.message.indexOf('ratelimited') !== -1
};
}
});
You can view the full code and a guide to deploying your own version here: https://autocode.com/src/jacoblee/slack-clear-messages/
Tip: if you gonna use the slack cleaner https://github.com/kfei/slack-cleaner
You will need to generate a token: https://api.slack.com/custom-integrations/legacy-tokens
If you like Python and have obtained a legacy API token from the slack api, you can delete all private messages you sent to a user with the following:
import requests
import sys
import time
from json import loads
# config - replace the bit between quotes with your "token"
token = 'xoxp-854385385283-5438342854238520-513620305190-505dbc3e1c83b6729e198b52f128ad69'
# replace 'Carl' with name of the person you were messaging
dm_name = 'Carl'
# helper methods
api = 'https://slack.com/api/'
suffix = 'token={0}&pretty=1'.format(token)
def fetch(route, args=''):
'''Make a GET request for data at `url` and return formatted JSON'''
url = api + route + '?' + suffix + '&' + args
return loads(requests.get(url).text)
# find the user whose dm messages should be removed
target_user = [i for i in fetch('users.list')['members'] if dm_name in i['real_name']]
if not target_user:
print(' ! your target user could not be found')
sys.exit()
# find the channel with messages to the target user
channel = [i for i in fetch('im.list')['ims'] if i['user'] == target_user[0]['id']]
if not channel:
print(' ! your target channel could not be found')
sys.exit()
# fetch and delete all messages
print(' * querying for channel', channel[0]['id'], 'with target user', target_user[0]['id'])
args = 'channel=' + channel[0]['id'] + '&limit=100'
result = fetch('conversations.history', args=args)
messages = result['messages']
print(' * has more:', result['has_more'], result.get('response_metadata', {}).get('next_cursor', ''))
while result['has_more']:
cursor = result['response_metadata']['next_cursor']
result = fetch('conversations.history', args=args + '&cursor=' + cursor)
messages += result['messages']
print(' * next page has more:', result['has_more'])
for idx, i in enumerate(messages):
# tier 3 method rate limit: https://api.slack.com/methods/chat.delete
# all rate limits: https://api.slack.com/docs/rate-limits#tiers
time.sleep(1.05)
result = fetch('chat.delete', args='channel={0}&ts={1}'.format(channel[0]['id'], i['ts']))
print(' * deleted', idx+1, 'of', len(messages), 'messages', i['text'])
if result.get('error', '') == 'ratelimited':
print('\n ! sorry there have been too many requests. Please wait a little bit and try again.')
sys.exit()
Here is a great chrome extension to bulk delete your slack channel/group/im messages - https://slackext.com/deleter , where you can filter the messages by star, time range, or users.
BTW, it also supports load all messages in recent version, then you can load your ~8k messages as you need.
There is a slack tool to delete all slack messages on your workspace. Check it out: https://www.messagebender.com